Many APIs require some form of authentication. A very common form used with many cloud providers and commercial APIs is OAuth 2.0. In this post, I want to give you an introduction to how OAuth 2.0 works and use it to authenticate with Microsoft Azure services.
OAuth 2.0 - Overview
The OAuth 2.0 specification is not what you would commonly describe as ‘easy to understand’. Eran Hammer, the former lead author for the OAuth 2.0 project actually resigned and removed his name from the specification. He even wrote a Medium post titled ‘OAuth 2.0 and the Road to Hell’. His core concern is that OAuth 2.0 might be too complex for most developers to implement securely. However OAuth 2.0 is more or less the de facto authentication standard and used by Google, Facebook and Microsoft to secure access to their APIs, so we have to learn to work with it to a certain extend.
OAuth is not to be confused with OpenID. “OpenID is about authentication (i.e. I can identify myself with an url) whereas OAuth is about authorization (i.e. I can grant permission to access my data on some website to another website, without providing this website the authentication information for the original website)”.
So how does OAuth 2.0 work conceptually? At its core, OAuth defines four roles:
- resource owner: An entity who can grant access to a protected resource (often the end-user)
- resource server: The server hosting the protected resource
- client: An app that wants to access protected resources with authorization from the resource owner
- authorization server: The server issuing access tokens to client after resource owner is successfully authenticated and authorization is obtained.
The protocol flow looks like this (taken from ietf.org):
+--------+ +---------------+ | |--(A)- Authorization Request ->| Resource | | | | Owner | | |<-(B)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(C)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(D)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(E)----- Access Token ------>| Resource | | | | Server | | |<-(F)--- Protected Resource ---| | +--------+ +---------------+
Note that the authorization server and the resource server do not need to be distinct servers. In practice, the authorization request is not made directly to the resource owner, but handled by the authorization server. The client gets an authorization grant which it can use to get an access token to access protected resources.
There are four different authorization grant types:
- authorization code,
- resource owner password credentials and
- client credentials
It is possible to define additional custom grant types as well. Let’s look at them in more detail:
The implicit grant is an authorization flow optimized for clients implemented in a browser or any app that cannot protect a client secret. The implicit grant flow is the same as the authorization grant flow above, but instead of getting an authorization code first that is exchanged for an access token after presenting the client secret with the authorization code, the authorization server returns an access token immediately.
Resource Owner Password Credentials
In this case we use the resource owner credentials directly to login to a given service. Since the app obviously collects password and user name from resource owner, this grant flow should only be used when the application is trusted to a very high degree by the resource owner. However, credentials do not need to be stored as the client gets an access token from the authentication server.
When the authorization scope is limited to the resources owned by the client, the client can use its client id and client secret to request an access token. This grant flow is more or less the same as the resource owner password credentials flow, except that the client is under control of the protected resources.
Access and Refresh Tokens
Access tokens allow access to protected resources and are usually short lived to minimize harm in case they are leaked. Refresh tokens on the other hand are usually long lived and can be used to request new access tokens after the old ones have expired.
You can find more details about OAuth 2.0 on the official OAuth webpage. I also found ‘OAuth 2 Simplified’ and ‘A Guide To OAuth 2.0 Grants’ by Aaron Parecki really helpful (He also gave a talk about “The vowel R”, which as an R programmer I find pretty cool:).
So, after we covered some basic theory about how OAuth 2.0 works, let’s get our hands dirty and try to use our knowledge to access APIs in Azure.
OAuth 2.0 on Azure:
To authenticate with Azure the following call is made to the
/authorize endpoint (line breaks for readability only):
https://login.microsoftonline.com/common/oauth2/authorize? response_type=code &client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46 &redirect_uri=http://localhost:8400 &state=mkb28apjvs1tj4m8m4hb &resource=https://management.core.windows.net/ &prompt=select_account
The client_id in the example above is from the Azure CLI client. Also note that compared to the example we had earlier we are accessing
/common/ instead of
/<tenant-id>/. Pasting the above URL into your browser will return a redirect url of the following form:
Before we start: In Azure there are two types of apps you can register: native and web apps. Only web apps have a client_secret parameter.
Let’s try to get access tokens for the Azure resource manager in R:
library(httr) app_name = "test" # client_id of Azure CLI client_id = "04b07795-8ddb-461a-bbee-02f9e1bf7b46" client_secret = NULL resource_uri = "https://management.core.windows.net/" # Endpoints are of the form: # https://login.windows.net/<common | tenant-id>/oauth2/<authorize | token> azure_endpoint = oauth_endpoint(authorize = "https://login.windows.net/common/oauth2/authorize", access = "https://login.windows.net/common/oauth2/token") azure_app = oauth_app( appname = app_name, key = client_id, secret = client_secret ) token <- oauth2.0_token(azure_endpoint, azure_app, user_params = list(resource = resource_uri), use_oob = FALSE )
You should now be redirected to a browser window where you can sign-in to your Azure account. There is also a library called AzureAuth as part of the cloudyR project, but I like that we can cover the entire flow with
httr and no further dependencies.