Understanding OAuth 2.0

Syed Sirajul Islam Anik
7 min readJun 14, 2020

--

Image from: https://www.webfarmr.eu/wp-content/uploads/2019/04/matt-artz-353284-unsplash.jpg

OAuth is an open standard for authorization. Authorization & Authentication is not the same thing. Authentication means who you are, whereas the Authorization defines what permissions you have. So, when you log into your medium account, that’s the authentication. And when you want to edit an article that doesn’t belong to you, then that’s because of the authorization.

OAuth is a standard that defines a set of rules to authorize resources. Take the Medium as an example. When you try to log in or create a new account, you see “sign in with Facebook” or “sign in with Google” which means, if you have an account with these providers (Facebook/Google), then you can let medium be a client to use your profile (resource) from any of those providers. This is where the OAuth fits in. OAuth 1.0 is officially deprecated and the latest version of OAuth is 2.0.

OAuth defines a set of rules that have to be followed by the clients (medium) and the providers (Facebook/Google). So, what are these rules?

Terms

  • Resource Owner: You, the entity who is capable of granting the protected resource authorization.
  • Resource Server: A server that hosts the protected resources (assume your profile)
  • Client: A client is an application that wants to access the resource from the Resource Server on behalf of the Resource Owner.
  • Authorization Server: The authorization server is a server that issues tokens for the client after the resource owner authorized the access. The authorization server & the resource server doesn’t mean to be physically different servers. They can reside on the same server.
  • Scopes: Scopes are the portion the Authorization Server wants to give access to the resource. If the authorization server defines scopes like “profile.read”, “comments.read”, “profile.update” then it means, it’ll limit the access to the resource.
  • Redirect URI: After authorizing or denying access, the client application will be redirected to that endpoint.
  • Client ID: A public-facing identity for the client.
  • Client Secret: A private secret for the client.

When you want to use the OAuth 2.0, you’ll need to register your application/client with the Authorization Server. The server then gives you a client id and client secret. When registering the client, you may require to select the scopes but you must have to provide the redirect uri.

Grant types

The authorization to a resource is acquired by an access token. To issue an access token on behalf of the resource owner can be done in Four Grant types.

  • Authorization code grant
  • Implicit grant
  • Resource Owner Password Credentials grant
  • Client Credentials grant

Let’s see how this happens. The URLs may vary based on the service providers. But the params are mostly the same.

Authorization code grant

Authorization code grant is a very popular grant type. If you ever used to log in somewhere with Facebook, Google, Twitter, or Github then it had used the Authorization code grant to gain access to your profile (resource). The authorization code grant flow requires two steps.

Step 1:

First, you need to redirect your user to the authorization server with the required parameters.

response_type=code
client_id=REGISTERED_CLIENT_ID
redirect_uri=REGISTERED_REDIRECT_URI
scope=IF_REGISTERED_SCOPES
state=RANDOM_STRING_BY_YOUR_APP

So, the URL will be something like

https://authorization.server/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=scopes&state=state

And, if your client is registered and the resource owner is logged in then it’ll show something like below which depends on the service provider. But if the user is not logged in, then he’ll first be prompt to the log in screen on the authorization server. And later will be redirected here.

User is prompt to decide to delegate the authorization

Then if the user decides to authorize, the authorization server will redirect the request to the redirect_uri endpoint with code and state previously given.

Step 2:

If the redirected uri contains the code query in the query parameter, then the client application server makes the next call to get the access token for the resource owner. That request contains the following

grant_type=authorization_code
client_id=REGISTERED_CLIENT_ID
client_secret=CLIENT_SECRET_FROM_AUTHORIZATION_SERVER
redirect_uri=REGISTERED_REDIRECT_URI
code=FROM_THE_REDIRECTED_URI_QUERY_PARAM

So, you’ll then have to make a new post request to the authorization server with the above parameters. Then if the server can identify yourself as a valid client and the code is correct, then it’ll return a response with access_token, token_type (mostly Bearer), expires_in, refresh_token.

  • Request for the access token
curl -XPOST 'https://authorization.server/oauth/token' \
--header 'Accept: application/json' \
--form 'grant_type=authorization_code' \
--form 'client_id=CLIENT_ID' \
--form 'client_secret=CLIENT_SECRET' \
--form 'redirect_uri=REDIRECT_URL' \
--form 'code=CODE_FROM_REDIRECT_URL'
  • Response for the access token
{
"token_type": "Bearer",
"expires_in": INT_TIME_IN_SECONDS,
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN"
}

So, now you can make requests to the resource server with the access token to get the resource. The access tokens are short-lived. So, to get an access token when expired, you will need to store the refresh token.

  • Request the resource server with the access token
curl -XGET 'https://resource.server/api/posts' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer ACCESS_TOKEN'

Now, the redirect_uri is not mandatory. But if the parameter is passed, then the value should match with the registered one.

Implicit Grant

In the authorization grant, we saw that we needed to follow two steps. But in implicit grant, it’s done in one step. Step two is not used in this grant.

This grant is almost similar to the Authorization grant. The required parameters to redirect to the Authorization server is the same except the response_type value.

response_type=token
client_id=REGISTERED_CLIENT_ID
redirect_uri=REGISTERED_REDIRECT_URI
scope=IF_REGISTERED_SCOPES
state=RANDOM_STRING_BY_YOUR_APP

So, the URL will be something like

https://authorization.server/oauth/authorize?response_type=token&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=scopes&state=state
User is prompt to decide to delegate the authorization

If the user accepts to authorize the request, then the client will be redirected to the redirect_uri endpoint with the access_token, token_type, and expires_in in the URL hash fragment. This grant response doesn’t contain the refresh_token.

Previously, it was intended for the browser-based applications (SPA), as the access token is returned immediately by the authorization servers and as they can’t keep the client secret. But, nowadays, it’s recommended to stop using this grant.

Resource Owner Password Credentials Grant

Resource Owner Password Credentials grant is the same as the Progressive Username & Password authentication. You need to pass the username & password along with the client id & client secret. In return, the authorization server returns an access token.

grant_type=password
client_id=REGISTERED_CLIENT_ID
client_secret=REGISTERED_CLIENT_SECRET
username=PROVIDED_USERNAME
password=PROVIDED_PASSWORD
scope=IF_REGISTERED_SCOPES
  • Request for the access token
curl -XPOST 'https://authorization.server/oauth/token' \
--header 'Accept: application/json' \
--form 'grant_type=password' \
--form 'client_id=CLIENT_ID' \
--form 'client_secret=CLIENT_SECRET' \
--form 'username=PROVIDED_USERNAME' \
--form 'password=PROVIDED_PASSWORD'
  • Response for the access token
{
"token_type": "Bearer",
"expires_in": INT_TIME_IN_SECONDS,
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN"
}

This type of grant is used only for first-party trusted clients.

Client Credentials

This grant doesn’t need any user (resource owner) interaction to authorize. That means that this grant type is not associated with the user. When the internal services want to communicate with each other or if you want to pull any data from third-party applications, then you can use this grant type.

grant_type=client_credentials
client_id=REGISTERED_CLIENT_ID
client_secret=REGISTERED_CLIENT_SECRET
scope=IF_REGISTERED_SCOPES
  • Request for the access token
curl -XPOST 'https://authorization.server/oauth/token' \
--header 'Accept: application/json' \
--form 'grant_type=client_credentials' \
--form 'client_id=CLIENT_ID' \
--form 'client_secret=CLIENT_SECRET'
  • Response for the access token
{
"token_type": "Bearer",
"expires_in": INT_TIME_IN_SECONDS,
"access_token": "ACCESS_TOKEN"
}

Refresh Token Grant

In the previous grants, when the response was returned for the access_token there was also an expires_in which denotes the number of seconds when the access_token will expire. So, whenever we found the refresh_token in the response, we have to keep it into a persistent store so that we can get access to the resource server whenever the access_token hits expiry. The access_tokens are short-lived. The refresh_tokens live longer than the access_tokens. To get new access_token to access resources, we need to use the resource grant. The usage of the Refresh Grant is as follows.

grant_type=refresh_token
client_id=REGISTERED_CLIENT_ID_FOR_THIS_TOKEN
client_secret=REGISTERED_CLIENT_SECRET_FOR_THIS_TOKEN
refresh_token=STORED_REFRESH_TOKEN

The refresh_token was given against specific client id & client secret. If you use different client_id and client_secret for the refresh token, it’ll then generate an error.

Authorization code grant with PKCE

In the Implicit Grant type, we saw that we don’t need any client secret to get the access token. But it’s recommended to replace implicit with the Authorization Code Grant with PKCE (Proof Key for Code Exchange). In the Implicit Grant, we don’t have to share any secret key but with Authorization Code Grant we had to share a secret when trying to get the access_token for an authorized code. PKCE stays in between the Authorization Code Grant and the Implicit grant.

You will have to share a code_challenge and code_challenge_method when redirecting a user to the authorization server. And share code_verifier when retrieving an access_token against an authorization code.

code_verifier = a random string of 43 to 128 characters
hash = sha256 hash of code_verifier
code_challenge
= base64 url encode of hash
code_challenge_method
= any of S256 or plain

So, Step one becomes like

response_type=code
client_id=REGISTERED_CLIENT_ID
redirect_uri=REGISTERED_REDIRECT_URI
code_challenge=CODE_CHALLENGE_FROM_ABOVE_SNIPPET
code_challenge_method=ANY_CHALLENGE_METHOD_ABOVE_SNIPPET
scope=IF_REGISTERED_SCOPES
state=RANDOM_STRING_BY_YOUR_APP

So, the URL will be something like

https://authorization.server/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=scopes&state=state&code_challenge=CODE_CHALLENGE&code_challenge_method=CODE_CHALLENGE_METHOD

Now, if the user authorizes the request you’ll get code in your redirect_uri endpoint and you’ll follow step two like before but with one extra parameter code_verifier and removing the client_secret.

grant_type=authorization_code
client_id=REGISTERED_CLIENT_ID
code_verifier=CODE_VERIFIER_FROM_ABOVE_SNIPPET
redirect_uri
=REGISTERED_REDIRECT_URI
code=FROM_THE_REDIRECTED_URI_QUERY_PARAM

After making a new post request to the authorization server upon success, it’ll return a response with access_token, token_type, expires_in, refresh_token.

  • Request for the access token
curl -XPOST 'https://authorization.server/oauth/token' \
--header 'Accept: application/json' \
--form 'grant_type=authorization_code' \
--form 'client_id=CLIENT_ID' \
--form 'code_verifier=CODE_VERIFIER' \
--form 'redirect_uri=REDIRECT_URL' \
--form 'code=CODE_FROM_REDIRECT_URL'
  • Response for the access token
{
"token_type": "Bearer",
"expires_in": INT_TIME_IN_SECONDS,
"access_token": "ACCESS_TOKEN",
"refresh_token": "REFRESH_TOKEN"
}

Notes

  • The grant types that required user interaction, has redirect_uri.
  • If a refresh_token is given and not stored then when the access_token hits its expiry, you can’t access the resource.
  • If the access_token has expired, and the refresh_token was stored but expired too, it’s impossible to get access to the resource server without the user’s authorization again.
  • Whenever the client secrets were used, they all are post requests.
  • The grant_type in the requests should match exactly as they’re written. They are fixed characters.
  • Is it a protocol or framework? Check this out.
  • Is OAuth SSO? Check this out.
  • Use client_credentials only if you don’t need any user interactions.

That’s all for OAuth 2.0. Happy coding ❤

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote