top of page

OAuth 2.0 Explained in Simple Words - Part III

Updated: Mar 4, 2023

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 II to look at some advanced concepts, specifically the PKCE grant.


1. Authorization Code with PKCE (Proof Key for Code Exchange)

Authorization Code with PKCE (pronounced 'pixy'), or simply PKCE grant, is designed to be used with SPA (Single Page Apps) or Mobile Apps as a modern solution. Before we dive in, let's look at some pre-knowledge to better understand it.


Public Client and Confidential Client

According to OAuth specs, there are two types of Clients that can be registered with an Authorization Server: Public Client and Confidential Client


Confidential Client - Client Applications that are able to securely store Client Secret and keep it safe

Public Client - Client Applications that NOT able to store Client Secret and hence, can NOT use secret to authenticate a Client


Based on the definition, when registering a Confidential type Client, a Client Secret will be provided together with the Client ID by the Authorization Server, while in contrast, NO secret will be provided to Public Client.


The reason for the above is that Confidential Client refers to Applications within a company, which usually have a backend using technologies like Java, .NET, NodeJS and etc. These applications can store a password or secret securely e.g. in a database.


On the other hand, Public Client refers to SPA or Mobile Application. SPA is written in JavaScript and usually has NO way to store a password/secret other than source code. Native Mobile Applications can NOT store password/secret securely either. The binaries from one's cellphone can be decompiled and credentials could be compromised.


The Issue with Public Client

Remember the standard Authorization Code flow is a two-step workflow, where the first step is a browser redirect to get an Authorization Code and second step is to exchange the code for Access Token with Http POST. Specifically, the second step needs to POST Client ID and Client Secret in addition to the Authorization Code.


If the Client App is a SPA or Native Mobile App (meaning a Public Client), it can NOT store secret and hence can NOT use Client ID and Secret to authenticate itself in the Authorization Server.


To avoid the issue of storing secret, one option is to use Implicit Grant instead of Authorization Code grant, where only Client ID and Redirect URI are used to authenticate the client in Authorization Server (NOT a secure way). Furthermore, since Access Token is returned in the URL, it has added risk of exposing the Access Token. This is why Implicit Grant is deprecated.


Now, if we go back to the Authorization Code grant and remove the Client Secret, only relying on Authorization Code, Client Id and Redirect URI to exchange for an Access Token. It looks plausible but actually is vulnerable to a type of attack called Authorization Code Interception Attack.


To fix this, some additional parameters: Code Verifier, Code Challenge and Code Challenge Method are added to the flow and this is the PKCE flow.


2. How PKCE Flow Works

Let's first understand what are Code Verifier, Code Challenge and Code Challenge Method.


Code Verifier - A cryptographically random string generated by the Client App. This value will be different each time a PKCE flow is initialized.

Code Challenge - This is the hashed value from Code Verifier (You need to understand what Hashing is).

Code Challenge Method - The hash method/function used to hash Code Verifier to generate Code Challenge


Note the default Code Challenge Method is 'plain', which does NOT do any hashing on the original Code Verifier and it is recommended to replace it with some hashing algorithm such as S256 (SHA256).



On a high level, the flow is:

  1. Client generates a random string (Code Verifier), and then hash it with a certain hash function(Code Challenge Method). Then the Client sends the hashed value (Code Challenge) and hashing method to the Authorization server.

  2. Authorization Server stores the Code Challenge value and Code Challenge method and returns the Authorization Code to Client.

  3. In exchanging Authorization Code for Access Token, the Client sends the Authorization Code together with, this time, Code Verifier to the Authorization Server.

  4. Authorization Server runs a hashing check with the Code Verifier and the Code Challenge & Code Challenge Method received in the first step.

  5. If the check is valid, Access Token will be returned.



3. PKCE Flow Steps

1. Client sends (browser redirect) code_challenge and code_challenge_method to Authorization Server


https://authorization-server.com/oauth/authorize?client_id=test-client123&response_type=code&scope=read&redirect_uri=https://oauth-app.com/callback&code_challenge=bc6c6abccd6fec88f1772fa263f824208db050306562e80f3f8684a82f6b9818&code_challenge_method=S256&state=abc123

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. Also some Authorization Server (such as ForgeRock) supports 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.

  • client_id - The registered Client Name/ID from the Authorization Server

  • response_type=code - This tells the Authorization Server that the Client App is initializing the Authorization Code flow (PKCE is a modified Authorization Code flow)

  • scope - One ore more strings (separated by space by default) indicating which permissions the application is requesting

  • redirect_uri - The URI where Authorization Server will redirect to after authenticating the user

  • code_challenge - Hashed value of original Code Verifier

  • code_challenge_method - Hash algorithm used to generate the code_challenge; 'S256' means SHA256

  • 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

2. Authorization Server returns Authorization Code


Based on the Authorization Code flow (see OAuth 2.0 Explained in Simple Words - Part II), user needs to authenticate in the Authorization Server and consent might be needed based on the server configuration.


The return of the Authorization Code happens in the URL parameter and will be an Http browser redirect.

https://oauth-app.com/callback?code=LtwzXMel17MNjrel4KIcaZoz24eGsLtQ&state=abc123
  • 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

3. Exchanging Authorization Code for an Access Token

Client sends out the Authorization Code and Code Verifier with an Http POST. You can see NO Client Secret is included in this request.


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=LtwzXMel17MNjrel4KIcaZoz24eGsLtQ 
&redirect_uri=https://oauth-app.com/callback 
&client_id=test-client123
&code_verifier=LtwzXMel17MNjrel4KIcaZoz24eGsLtQ

The client_id and redirect_uri must match the previous call.

  • grant_type=authorization_code - This tells the Authorization Server that the Client app is using the Authorization Code flow(again, PKCE is still a modified 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

  • code_verifier - The original string used to generate the code_challenge


4. Authorization Server Check code_verifier, code_challenge and code_challenge_method

Besides checking the code, redirect_uri and client_id, the Authorization Server will do a hashing on code_verifier with code_challenge_method and compare the generated value with the code_challenge value sent by client. If the two values does NOT match, Authorization Server will reject the request and terminate the flow.


5. Authorization Server Returns Access Token

After validation of all parameters and hashing, Authorization Server will return the Access Token in the payload.

HTTP/1.1 200 OK 
Content-Type: application/json 
Cache-Control: no-store 
Pragma: no-cache
 
{
  "access_token":"eyJ0eXW13A8OZ5AbigKfDmKAPHHGTU7cUbNdrw...",
  "token_type":"Bearer",
  "expires_in":3599,
  "scope":"read"
}

Similar to Authorization Code flow, Refresh Token can be issued by Authorization Server as well.


4. Sum Up

In this post, we talked about the PKCE flow and why it is used as a modern solution for SPA and Mobile App. It is a modified Authorization Code flow to address secret storing issue with the Public Client. The details steps in action for PKCE is shown as well.


You can continue reading in the OAuth 2.0 Explained in Simple Words - Part IV

85 views

Recent Posts

See All
bottom of page