Want to know how OAuth 2.0 works? Read on and find it out.
This is a series of blogs exploring OAuth and OIDC:
OAuth 2.0 Explained in Simple Words - Part I: What and Why
OAuth 2.0 Explained in Simple Words - Part II: OAuth 2.0 Flows, Authorization Code Flow and Client Credentials Flow
OAuth 2.0 Explained in Simple Words - Part III: OAuth 2.0 Flows, PKCE Flow
OIDC Explained in Simple Words - Part I: OIDC as an OAuth 2.0 Enhancement
OIDC Explained in Simple Words - Part II: OIDC as an Modern SSO Standard
In this post, we continues from OAuth 2.0 Explained in Simple Words - Part I. We will be looking at different grant types in OAuth 2.0.
1. Client Registration
Before the client can interact with the Authorization Server, it needs to be registered in the Authorization Server to provide the following details:
Client ID
Client Secret
Redirect URI or Callback URL (could be multiple)
Client ID and Client Secret are similar to Username and Password. The former is used for Application and the latter is used for human user. By providing the client id and secret to the Authorization Server, it knows the client is a legitimate one to trigger the OAuth flow.
The Redirect URI is where the Authorization Server will do a http browser redirect to after the user authenticates in the Authorization Server.
2. OAuth 2.0 Grant Types
In part I, we talked about the OAuth 2.0 high level workflow. Now we will dive in with more details and one important part is to understand different grant types. Different grant types are just different workflows to get an Access Token and based on the scenario, we should pick the proper grant type.
Here is the list of Grant Types:
Authorization Code
Client Credentials
PKCE
Device Code
Refresh Token
Implicit (deprecated)
User Password (deprecated)
The first two, Authorization Code and Client Credentials, are the most often used. Authorization Code is used when an End User is involved (user-to-machine flow) while Client Credentials is used when NO End User is present (machine-to-machine flow).
The last two, Implicit and User Password are legacy flow and will be deprecated in the future.
In this post, we will go through the Authorization Code and Client Credentials workflow, as those two are crucial to understand OAuth 2.0.
3. Authorization Code Grant
The Authorization Code grant type is the most commonly used. There are mainly two parts in the process:
User authenticates to Authorization server and Authorization Server returns Authorization Code to the Client (Browser based)
Client uses Authorization Code to exchange an Access Token (No Browser)
Part one will be based on browser redirect and part two will be an Http Post where NO browser is involved.
Note, some Authorization Server (such as ForgeRock) supports step 1 - getting Authorization Code without Browser, meaning that Client can make REST call to its endpoint providing the same parameters as payload and get back the authorization code. This depends on the Authorization Server.
Step 1. User Accesses the Client.
User interacts with the Client app, which will trigger an Authorization Code grant workflow.
Step 2. Client Starts the Authorization Code
Remember the first part of the flow is browser-based, so it will start with an URL with some parameters.
https://authorization-server.com/oauth/authorize?response_type=code&client_id=test-client123&redirect_uri=https://oauth-app.com/callback&scope=read&state=PPKswuzDbVL06ankyPO6FoVwi1CdysR5
Notice that https://authorization-server.com/oauth/authorize is the Authorization Server address with the endpoint to start the flow. This is defined by the Authorization Server itself.
response_type=code - This tells the Authorization Server that the Client App is initializing the Authorization Code flow
client_id - The registered Client Name/ID from the Authorization Server
redirect_uri - The URI where Authorization Server will redirect to after authenticating the user
scope - One ore more strings (separated by space by default) indicating which permissions the application is requesting
state - A random string to keep track of current OAuth 2 flow transaction; the same value will be returned by the Authorization Server; this is to prevent CSRF attacks
Step 3. User Authentication
In this step, User needs to log in to the Authentication Server. OAuth 2.0 does NOT specify what kind of authentication method is required, so it all depends on the need. Sometimes a simple username/password will do, while other times, MFA (Multi-Factor Authentication) is needed.
After user is authenticated, Authorization Server might present a consent page to list out permission that the Client App is requesting to access. If user denies it, the flow will be terminated.
The consent page is optional and a lot of times, the Authorization Server can turn it off to indicate an 'implicit-consent'. This suits some use cases well.
Step 4. Authorization Server Returns the Authorization Code to the Client App
The return of the Authorization Code happens in the URL parameter and will be an Http browser redirect.
https://oauth-app.com/callback?code=2ummcKJvTt3ocgeI3RtdUOEWtZ6wMhmT&state=PPKswuzDbVL06ankyPO6FoVwi1CdysR5
code - This is the authorization code generated by the Authorization Server; it is usually short-lived (between 1-10 mins) and will be invalid once used
state - This will be the same value from the request; if the Client App sees a different value, the flow should be terminated
Step 5. Exchange Authorization Code for an Access Token
Once the Authorization Code is retrieved by the Client App, it will make an Http POST request to the corresponding Authorization Server endpoint to exchange the Authorization Code for an Access Token.
To send out the POST request, you can use CURL from command line or REST Client Tool like POSTMAN. The POST request will look like below:
POST /oauth/token HTTP/1.1
Host: authorization-server.com
grant_type=authorization_code
&code=2ummcKJvTt3ocgeI3RtdUOEWtZ6wMhmT
&redirect_uri=https://oauth-app.com/callback
&client_id=test-client123
&client_secret=xxxxxxxxxx
grant_type=authorization_code - This tells the Authorization Server that the Client app is using the Authorization Code flow; do NOT confuse with 'response_type=code' in step 2
code - The same as the value returned by the Authorization Server
redirect_uri - The same redirect URI that was used when requesting the code; this may not be required depending on the Authorization Server
client_id - The same client id that is used to request the code
client_secret - The secret with the registered Client to make sure it is a legitimate Client
Step 6 Authorization Server Returns an Access Token
All the parameters sent by the Client App will be verified by the Authorization Server and then it will send an Http response with the Access Token in the response body.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...",
"token_type":"Bearer",
"expires_in":3599,
"scope":"read",
"refresh_token":"eyJ0eXAiOiJKV1QiAqdFSBzjqfTGAM4B4..."
}
Note a Refresh Token is included in the response as well. Access Token usually expires in a couple of hours and then the Client App can use the Refresh Token to call another Authorization Server endpoint get a new Access Token, without having the same End User to log in the Authorization Server again.
Step 7 Client App Access Resource Server with Access Token
Next, the Client App requests access to the protected resources on the Resource Server.
The Access Token is a bearer token, meaning it is self-contained. Any client possessing this token will be authenticated as long as the token itself is still valid.
The Access Token is put in the Http request Header with header name 'Authorization' and value 'Bearer ' (word 'Bearer' plus a space) inserted before the actual token. The word 'Bearer' is case-insensitive.
Authorization: Bearer eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...
Step 8 Resource Server Verifies the Access Token
Resource Server must always verify the Access Token before granting the Client App the access. Depending on the type of the Access Token, there are different ways to verify it.
One way is to send the Access Token back to the Authorization Server endpoint (introspection) and let it do the work. Another way is that if the Access Token is a JWT (a lot of times is), the Resource Server can decode the Access Token payload and verify expiration, scope, issuer as well as signature.
Token Introspection
By default, a Client can only introspect its own Access Token. Depending on the Authorization Server, a Client can introspect an Access Token issued to other Clients with some special scope values.
POST /oauth/introspect HTTP/1.1
Host: authorization-server.com
token=eyJ0eXAiOiJKV1QiLCciOiJSUzI1NiIs...
The Authorization Server will respond with the Access Token information.
{
"active": true,
"scope": "read",
"client_id": "test-client123",
"token_type": "Bearer",
"exp": 1419356238
}
There are other endpoints as well such as revoking an Access Token, Jwks and .wellknown endpoints. We'll look more on this in OIDC.
Step 9 Resource Server Returns Resources to the Client App
Once the Access Token is verified, the Resource Server will send the requested resources back to the Client App and the flow is finished.
4. Client Credentials Grant
The client credentials grant is used when Client App is calling some API resources hosted by the Resource Server. This is considered a machine-to-machine (or server-to-server) call as opposed in the Authorization Code grant where user is involved.
Client Credentials Grant process is simpler than Authorization Code, where NO redirect is needed. Client App sends the request with Http POST to the Authorization Server and after verification, it will return the Access Token.
Step 1. Client Initializes the Client Credentials Flow
This will be a straight Http POST call to the Authorization Server.
POST /oauth/token HTTP/1.1
Host: authorization-server.com
grant_type=client_credentials&client_id=test-client123&client_secret=xxxxxxxxxx&scope=read
Notice that https://authorization-server.com/oauth/token is the Authorization Server address with the endpoint to start the flow. This is defined by the Authorization Server itself.
grant_type=client_credentials - This tells the Authorization Server that the Client app is initializing the Client Credentials flow
client_id - The same client id that is used to request the code
client_secret - The secret with the registered Client to make sure it is a legitimate Client
scope - One ore more strings (separated by space by default) indicating which permissions the application is requesting
Step 2. Return with Access Token
All the parameters sent by the Client App will be verified by the Authorization Server and then it will send an Http response with the Access Token in the response body.
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"eyJ0eXAiOiJKV1QiLCJhbGSUzI1NiIs...",
"token_type":"Bearer",
"expires_in":3599,
"scope":"read"
}
Note NO Refresh Token will be included in the Client Credentials flow.
The remaining steps are the same as in Authorization Code grant flow.
5. Sum Up
In this post, we talked about Grant Types in OAuth 2.0. Authorization Code and Client Credentials are the often used grant types. The former involves a User and the latter has NO User but rather machine-to-machine call. Then we talked the details of the two flows. Besides those two, there are other Grant Types - PKCE, Device Code and Refresh Token. The Implicit and User Password grant types are legacy and will be deprecated.
You can continue reading in the OAuth 2.0 Explained in Simple Words - Part III