If you're building a web application that uses a third-party API, you're likely using the OAuth 2.0 code grant to obtain user permissions. However, its process might not be entirely clear to you. In this post, I'll demystify the process and guide you through the entire OAuth 2.0 code grant flow.
Setting up your application
Before you start working with OAuth 2.0, you first need to sign up your application on the service provider's website. Take Google's APIs as an example; you must register on the Google API Console.
You'll be asked for some simple details about your application, like its name, website, and logo. You'll also need to provide a redirect_uri
, which I'll explain in more detail later.
After this, you'll get a client_id
and client_secret
. These are important because you'll use them in the OAuth 2.0 process.
Complete OAuth 2.0 code grant flow
Once you've set up your application, you're ready to start using OAuth 2.0. Here's what the complete process looks like:
- Request authorization and get authorization code.
- Exchange authorization code for access token.
- Use access token to get user resource through API.
Let's dive into each step, using Google OAuth 2.0 as example.
1. Request authorization and get authorization code
The first step of the web flow is to request authorization from the user. This is accomplished by creating an authorization request link (a GET request) for the user to click on.
GET https://example-service.com/login/oauth/authorize
This endpoint usually takes the following query parameters.
-
client_id
: This is your app's unique identifier, which you receive after registering your app with the service. -
response_type
: This should be set tocode
, indicating that you're asking for an authorization code in response. -
state
: This acts as a security measure and should be a random value for each request. When the user is redirected back to your app, you should verify that the state value matches what you originally sent. -
redirect_uri
(optional): The URI where you want the user to be redirected after the authorization is complete. This must match the redirect URI that you have previously registered with the service. -
scope
(optional): Here, you can ask for different levels of access by including one or more scope values (separated by spaces). The specific values depend on the service you're using.
Following the link leads the user to a sign in page:
When the user signs in, the service presents a page detailing your request, including the name of your application, what access you're asking for, etc.
If the user agrees to proceed by clicking Continue, the service redirects them back to redirect_uri
, including code
(authorization code) and the same state
parameter you specified, in the query string.
Here's a flowchart that outlines this first step:
2. Exchange authorization code for access token
The second phase involves exchanging the code
you've received for an access_token
. This is done by making a POST request to the service's authorization server:
POST https://example-service.com/login/oauth/access_token
This endpoint usually takes the following query parameters.
-
grant_type
: This should be set toauthorization_code
. -
code
: The authorization code that you received. -
client_id
: The unique identifier for your app, provided when you registered with the service. -
client_secret
: A secret key you received upon registering your app, meant to authenticate your request. -
redirect_uri
(optional): If you included a redirect URI in the initial authorization request, it must also be included here and match exactly.
The service's response to this POST request will include the access_token
you'll use in the final step.
Here's a flowchart summarizing this step:
3. Use access token to get user resource through API
Once you have the access_token
, you might save it for later to make API calls. Include it in your request headers like this:
Authorization: `Bearer ${access_token}`
This informs the resource server that you are authorized to access the user's information.
Here's a flowchart illustrating this step:
Now that you understand the basic OAuth 2.0 code grant flow, let's review everything with a diagram showing the full process:
PKCE
The basic authorization flow works well, but it has a vulnerability: if an attacker manages to intercept the code
, they can exchange it for an access_token
. This poses a serious security risk, potentially allowing the attacker to access protected resources as the user.
This is where PKCE (Proof Key for Code Exchange) comes into play. PKCE is an extension to the basic authorization flow to prevent this kind of attack.
To implement PKCE, the application must first:
- creates a
code_verifier
. This is a cryptographically random string. - use
code_verifier
to generate thecode_challenge
by performing a SHA256 hash. If the application cannot perform a SHA256 hash, it should use the plaincode_verifier
as thecode_challenge
.
Next, you'll include additional parameters in the previous steps to integrate PKCE:
1. Request authorization and get authorization code
When requesting authorization, include these additional parameters:
-
code_challenge=XXXXXXXXX
: The code challenge generated as previously described -
code_challenge_method=S256
: This can be eitherplain
orS256
, indicating whether the challenge is the plaincode_verifier
string or a SHA256 hash of it.
The authorization server will associate code_challenge
and code_challenge_method
with the code
it generates.
2. Exchange authorization code for access token
When exchanging the code
for an access_token
, add the following parameter:
-
code_verifier
: The code verifier for the PKCE request, that the app originally generated before the authorization request.
Since the code_challenge
and code_challenge_method
were associated with the code
initially, the server should already know which method to use to verify the code_verifier
.
- For the
plain
method, the authorization server simply verifies that the providedcode_verifier
matches the previously storedcode_challenge
. - For the
S256
method, the authorization server will hash the providedcode_verifier
using SHA256 and then compare it to the storedcode_challenge
.
If the verification is successful and the values match, the server issues an access_token
. If they don't match, the request is denied.
This mechanism ensures that even if an attacker intercepts the code
, they cannot exchange it for an access_token
without the code_verifier
.
Conclusion
In summary, the OAuth 2.0 code grant flow is a crucial process for you to understand and implement when building web applications that require access to user data from third-party services. By following the outlined steps and incorporating PKCE, you can securely authenticate users and access necessary resources while protecting against common security vulnerabilities.
Top comments (1)
Very good article. A fully implemented OAuth 2 system in Spring and React is available at nidam.derbyware.com for a comprehensive solution.