The Definitive Guide to OAuth version 2.0

ARTICLE BY Matt Boisseau

What is OAuth? For that matter, what is authorization? Do you need it? How do you use it?

Everything you wanted to know about OAuth is in one convenient location: right here. Start with the basics or jump right to the info you need.

1. The Basics

COMMENT (BP): Let’s avoid empty sections like this

1.1. What is OAuth?

COMMENT (BP): Make your voice more personal. Here’s an example:

Before we dig into the details about OAuth, let's break down the name OAuth into it's pieces
and describe each one:

OAuth is an Open standard for Authorization.

Authorization is the act of granting permission. When an app wants to use your data (location, photos, documents, etc.), it has to ask for permission. You can choose whether or not to authorize the app, usually by clicking on Allow or Decline.

Standards are not hard restrictions or specific implementations. A standard is basically the set of rules that the industry has agreed is best.

Open means that the standard is publicly available and open for modification. You can read the OAuth 2.0 specification here. The specification defines what OAuth is and how it is implemented and used (along with a healthy ecosystem of recommended tweaks).

COMMENT (BP): Technically this partially correct. OAuth is a set of guidelines for how an application can ask ask a user for permission to various things like their data, resources or to take actions on their behalf. This permission is granted by the user through an authorization server. That server might be an identity provider or some other system that is the authoritative source of truth for that user

So, what is OAuth? It’s an ever-evolving set of guidelines for creating apps that can request permission from users.

1.2. How Does OAuth Work?

Imagine a fictional compression app, named Pied Piper, wants to let users compress their files in Google Drive.

Before OAuth, a common “solution” was for Pied Piper to collect each user’s Google username and password. Pied Piper could then log into Google Drive as the users, and grab the files to compress. And it doesn’t stop there. If Pied Piper wanted to later compress more files when the user was asleep, it would need to store the user’s Google username and password somewhere so that it could use it later. The username and password would have to be stored in plain-text in order for Pied Piper to use it in the future. If your first thought is, “I don’t want to give my Google password to random apps,” and your second thought is, “they are storing my username and password in plain text,” then you already understand why OAuth is a huge improvement.

With OAuth, Pied Piper can ask a trusted OAuth provider to get permission from the user to access their resources (i.e. files in their Google Drive).

COMMENT (BP): You mentioned FusionAuth in this section, but I think it is going to be really confusing. Let’s mention FusionAuth later and use Google here

Here’s the OAuth process of the user granting permissions to Pied Piper in a bit more detail:

COMMENT (BP): Diagram needed??

  1. Pied Piper registers with Google.

    Most apps don’t handle their own authorization; they use an OAuth provider, like Google. OAuth providers have put in a lot of work to make this easy for you (COMMENT (BP): who is “you” in this case? Should this be “developers”. Users’ trust in your OAuth provider is trust you don’t have to establish on your own.

  2. Pied Piper asks Google to get consent from the user.

    COMMENT (BP): FusionAuth doesn’t have pop-ups and most providers are moving away from them. I might talk about directing them to the OAuth Providers UI. From there, they might need to login, but they might also be already logged in. After they authenticate, they are asked for permission

    When the app wants to access a user’s data, it needs to ask for permission. The app redirects the user to its OAuth provider; this takes the familiar form of a popup asking the user to click “Allow.” If the user does click “Allow”, the OAuth provider gives the app a Grant, which is a receipt for the user’s consent.

  3. Pied Piper “logs in” with Google COMMENT (BP): I’d avoid using “logs in” here

    The app needs to prove that it got consent and that it is who it says it is. The app presents the Grant to the OAuth provider, as well as its Secret. If everything looks good to the OAuth provider, the app receives an Access Token, which is a ticket that allows the app into specific parts of the user’s data for a specific amount of time.

  4. Pied Piper makes an API call to Google Drive.

    The user’s data is accessible via a resource server, which is a locked API; the app needs a ticket to get in. When the app makes an API call, it also sends the Access Token; the resource server can verify the token’s authenticity with the same OAuth provider that issued it. If the Access Token is legit and hasn’t expired, the resource server will send back the data requested in the API call.

This workflow is a standard way for applications to integrate with third-party OAuth providers like Google. Usually this is necessary if the application wants to call APIs of the third-party as the user. Our example calls the Google Drive API as the user in order to access the user’s files. However, it could also be that the OAuth provider is not a third-party, but is just a part of the application (like a separate microservice). This case uses the same workflow, but the last step is not used. Instead, the Access Token is used by the application to call its own APIs.

COMMENT (BP): Do we need a diagram of the “Local OAuth Provider” to distinguish it from the “Third-Party Provider”? Or perhaps we break this into 2 sections. One for third-party OAuth and one for local OAuth. Then we could describe the local one in more detail.

1.3. How Do I Use OAuth?

COMMENT (BP): I feel like this is the main section of the article and it doesn’t explain the concepts enough or in an approachable way. It sorta says that they will be used later. If we are going to provide a guide, then we should be explaining everything up front in simple terms. Then we can get into details later, but only once the reader fully understands what OAuth is. As an example, we don’t really explain that the user is going to code a “Login” link from their App to the OAuth Provider. That’s a huge piece. There are no code examples here that would illustrate what that looks like or means. I sorta feel like this is divided into a very light explanation section followed by very technical integration section. It feels less like a Guide and more like a reference or a specification. Shouldn’t this be more like a walk-through? Like if you were going to build an application, how would you integrate OAuth with it.

COMMENT (BP): From Slack

I’m not convinced the reader will know what we are talking about. For example, in Section 1.3, the steps are really more like this:

1. Pick an OAuth Provider
2. Install it and get it running
3. Register your Application with it (in FusionAuth, you create an Application). This produces the client_id and client_secret
4. Determine how to send users from your Application to the OAuth Provider (usually via a login link and we should show code here)
5. The user Authenticates with the OAuth Provider (this is key, but not part of the spec and we need to explain why)
6. The user grants your Application permission (this step is optional actually. I think we need to explain that there are two types of OAuth integrations, a “internal” and a “third-party”. Most of our customers will be using “internal”. In this case, the user doesn’t need to grant permissions to the Application because the OAuth Provider is managed by the same developers that are building the Application. Therefore, once the user logs into the OAuth Provider, the permissions are implicitly granted)
7. The OAuth Provider sends the user back to your Application along with a code. (this is super key because it is where redirect_uri comes into play but we don’t really discuss how important that piece is)
8. The browser makes a GET request to the redirect_uri and this calls your Application backend.
9. The Application backend takes the code from the URL parameters and exchanges it for an access_token . This process requires the secret we got from the OAuth Provider in step 3.
10. The Application backend can now store the access_token in a session or push it back to the browser as a cookie or put it in the database or whatever it needs. This is also very important because it is how the user will be identified by the Application for the rest of their session or for a very long time. We also could explain refresh tokens here and in step 9.
11. Finally, the Browser (via AJAX) or the Applicaiton Backend can now call APIs as the user with the access_token. 

We can skip the OIDC extensions here and add them in a separate section. These include the id_token and the new APIs.

Overall, I think the information in the article is good, but the approach might leave a lot of readers confused. It seems like we need a “story”.

“Your a developer and want to use OAuth instead of building your own login system. How do you do that step-by-step? Once you have the core idea, we can explore variations and extensions.”

If you’re here, it’s likely that you’re interested in using OAuth in your own app. If you want to get up and running ASAP, check out FusionAuth’s 5-Minute Setup Guide.

We’ll assume that you are interested in leveraging OAuth to log users into your application and the OAuth provider will be a local identity provider. That is to say that the identity provider is used mainly for authentication rather than the Google example from above where the application wanted access to Google APIs as well. When setting up a local OAuth provider and integrating your application with it, the steps are as follows:

  1. Register your Application.

    COMMENT (BP): Need to let the reader know that this is a configuration/setup step

    After you pick an OAuth provider (like FusionAuth) and install it, you’ll need to configure the OAuth settings. Each OAuth provider is different, but the end result is the provider will ask for some basic details about your app.

    After you register your application with the OAuth Provider, it will produce two items you will use for the integration in the following steps: a client_id and client_secret. These are basically your app’s username and password and will be used later to ensure that only your application is able to communicate with the OAuth Provider.

    COMMENT (BP): This needs more explanation because it is super pivotal to understand what redirect URIs are, how they are configured with the OAuth Provider, why they must be defined (security), how they are sent on the login link, etc.

    You’ll also register a redirect_uri; the next step takes users away from your app, so your OAuth provider needs to know how to get them back.

  2. Authorization.

    When your app needs a user’s data, it will need to ask for permission.. Redirect the user to your OAuth provider’s authorize endpoint. Your OAuth provider will authenticate the user and ask them to click Allow or Deny.

    Assuming they click on Allow, the user will be redirected back to your app (via your redirect_uri). Your OAuth provider will also send you a code.

  3. Code Exchange.

    A code can be exchanged for an access_token. Make a POST request to your OAuth provider’s token endpoint. This is where your client_secret comes in; your OAuth provider will use this to authenticate your app.

    Now that both the app and user have proven their identities and agreed on sharing data, your OAuth provider will send you an access_token.

  4. API Request.

    The only reason you didn’t go straight to making API requests for the user’s data is because the API wants an access_token. Now that you have one, you can request away.

    When you send an access_token to the API, it can in turn send it to your OAuth provider for verification: “is this legit?” Once the API is satisfied, it will send you the user’s data.

As you read on, refer back to these steps. This is the skeleton of OAuth; some approaches (or “flows”) will add layers on top or remove some bones, but it’s always ultimately OAuth underneath.

2. Your App

There’s more than one way to implement OAuth. The way described in the previous section is the most common: Code Flow.

Before you dive in, you should figure out a few things about your app’s restrictions and requirements; maybe one of the other flows is a better fit.

2.1. Can Your App Keep a Secret?

2.1.1. Server-Based Apps

Server-based apps are also known as “confidential clients,” because they can keep a client_secret confidential. Typically, this means they run on a server, with only the UI running on users’ computers.

In practice, this means that, in order to use a client_secret, any requests to your Oauth provider’s token endpoint must come from the server your app is installed on.

2.1.2. Client-Based Apps

Client-based apps are also known as “public clients,” because they are installed or downloaded wholesale onto users’ computers, which means the client_secret is installed or downloaded onto users’ computers. Info stored on users’ computers is considered “public.” If the client_secret is public, even temporarily, it can be copied and used to impersonate your app.

In practice, this means that you’ll need to use an alternative method for authenticating your app; that’s what the PKCE extension is for. Check out Code Flow + PKCE.

2.2. How Do Users Interact With Your App?

2.2.1. Uh, the Normal Way?

If your users access your app with a mouse, keyboard, or touchscreen, then you don’t need to worry about Device Flow or Client Credentials Flow.

2.2.2. Through a Smart TV or Other IoT Device

We’ve all tried typing long usernames and passwords with a TV remote or game controller. It’s not fun. Luckily, there’s a flow just for that; Device Flow gives users a short code to type in on their phone or computer.

2.2.3. They Don’t

If your app doesn’t require human consent, it can still utilize OAuth to verify other apps or even itself. This is often referred to as M2M (machine to machine) interaction. You’ll be needing Client Credentials Flow.

3. Choosing an OAuth Flow

3.1. Code Flow

Recommended for: server-based (confidential) apps.

This is the most common and standard flow. It’s not unreasonable to say that the other flows are based on this one.

See how to implement this flow, step-by-step.

3.2. Code Flow + PKCE

Recommended for: client-based (public) apps.

PKCE (pronounced “pixie”) is an extension that allows client-based to use Code Flow. Because client-based apps can’t keep a client_secret, this flow has you generating and hashing a new, cryptographically random code_verifier for every authorization. Prior to PKCE, client-based apps had to use Implicit Flow, which is now deprecated.

See how to implement this flow, step-by-step.

3.3. Device Flow

Recommended for: apps installed on devices without keyboards.

If you use device flow, your app with show a user_code and verification_uri to the user. Your app will then periodically ask your OAuth provider if the user has navigated to verification_uri and entered user_code yet.

See how to implement this flow, step-by-step.

3.4. Client Credentials Flow

Recommended for: apps that don’t need human input.

Unsurprisingly, the biggest difference with this flow is that it skips the Authorization step, because there is no user to ask. This means that all your app has to do is trade its client_id and client_secret for an access_token.

See how to implement this flow, step-by-step.

3.5. User Credentials Flow

Not recommended for most apps.

This flow has your app taking users’ usernames and passwords directly, which is basically what OAuth is designed to prevent. The danger (from a user’s perspective) is that an app could store the credentials to use later, without the users’ consent. Still, this can be useful for apps that have built up a lot of trust with their users, such as those developed by OAuth providers, themselves.

See how to implement this flow, step-by-step.

3.6. Implicit Flow

Not recommended for most apps.

This flow is deprecated, and for good reason; it skips the Code Exchange step entirely, getting an access_token instead of a code. Because the Authorization step ends in a redirect, the access_token is exposed in the redirect URI parameters; these parameters can easily be copied out of a browser’s URL bar. Anyone who holds your access_token is able to make API requests as if they are your app.

Before CORS was widely available in modern browsers, this was the only way for client-based apps to avoiding exposing their client_secrets. Now, you’re better off using Code Flow + PKCE, unless your app is somehow unable to utilize CORS.

See how to implement this flow, step-by-step.

4. Authentication in OAuth

OAuth has no concept of identity; it only specifies methods of authorization. If you read the OAuth spec, you’ll see repeated disclaimers that OAuth is not an authentication framework. That doesn’t mean you don’t need it; it just means there’s a bit more to learn.

4.1. OpenID Connect

The standard for authentication in OAuth is OpenID Connect (OIDC).

OIDC is an authentication layer built on top of OAuth 2.0. If your OAuth provider is OIDC certified, you can get an id_token along with your code. The id_token is a signed JWT that contains user profile information, such as name, email address, phone number, etc. That profile information can be trusted due to the signature, which can be verified by using your OAuth provider’s userInfo endpoint.

More on JWTs can be seen in the Using Access Tokens section.

To indicate that you want an ID token, simply include openid in the scope of your authorization request.

5. Implementing OAuth Flows Step-by-Step

5.1. Code Flow

Not sure if this is the right flow for your app?

  1. Redirect the user to your OAuth provider’s authorize endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    redirect_uri your redirect URI tells your OAuth provider where to redirect users after authorization is done
    response_type code indicates that you’re expecting an authorization code in response
    scope (optional) space-separated list of access levels tells your OAuth provider which specific data you’re trying to get
    state random string used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://oauth-provider.com/oauth2/authorize?
         client_id=06494b74-a796-4723-af44-1bdb96b48875&
         redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin&
         response_type=code&
         state=E7bMSVO7DlxkFueN&
         scope=openid%20profile%20email
    

    When the user gets there, your OAuth provider will show a page with your app’s name and logo, the scope of the request, an Allow button, and a Decline button.

    Depending on the implementation, the user will also hit an authentication layer, in which they will log in with username-password, social media, biometrics, or whatever other options your OAuth provider allows.

  2. Your Oauth provider will redirect the user to your redirect_uri along with some parameters:

    parameter value purpose
    code the authorization code from your OAuth provider proof that the user gave consent
    state the same random string you gave in the previous step used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://www.piedpiper.com/login?
         code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg&
         state=E7bMSVO7DlxkFueN
    

    Verify that state is the same as the one you sent; if it’s not, someone other than your OAuth provider is trying to execute a CSRF attack. Save code for the next step; you’ll need it to get an access token.

  3. Make an HTTP request to your OAuth provider’s token endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    client_secret your client secret used by your OAuth provider to authenticate your app
    code the authorization code from your OAuth provider proof that the user gave consent
    grant_type authorization_code indicates that you’re using an authorization code as a grant
    redirect_uri the same redirect URI as you gave in step 1 used by the OAuth provider to verify your token request

    Example POST request:

     POST /oauth2/token HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     client_secret=_UctTBl5PG89-vCwrOo0FqYLywnUC4hSjx927sLjuzM&
     code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg&
     grant_type=authorization_code&
     redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin
    
  4. Your OAuth provider will respond with JSON, including the access_token:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    refresh_token (optional) an opaque token used to get new access_tokens after they expire

    Example JSON:

     {
         "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw",
         "expires_in" : 3600,
         "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ"
     }
    

    That’s the end of the Authentication Grant Flow! See Using Access Tokens for what to do next.

5.2. Code Flow + PKCE

Not sure if this is the right flow for your app?

  1. Generate a new code_verifier and code_challenge for every request. The verifier can be any cryptographically random string, and the challenge is a SHA-256 hashed version of that string. For most environments, libraries exist for both cryptographic randomization and SHA-256.

  2. Redirect the user to your OAuth provider’s authorize endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    code_challenge_method S256 indicates that you used SHA-256 to hash code_verifier into code_challenge
    code_challenge SHA-256 hashed version of code_verifier will be compared with code_verifier in a later step, which allows your OAuth provider to authenticate your app
    redirect_uri your redirect URI tells your OAuth provider where to redirect users after authorization is done
    response_type code indicates that you’re expecting an authorization code in response
    scope (optional) space-separated list of access levels tells your OAuth provider which specific data you’re trying to get
    state a random string used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://oauth-provider.com/oauth2/authorize?
         client_id=06494b74-a796-4723-af44-1bdb96b48875&
         redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin&
         response_type=code&
         code_challenge=E8AD0F7972B90735CDF7E12CB5623027E33DE10B7E7956A8C77E32C16B9532F9&
         code_challenge_method=S256&
         state=E7bMSVO7DlxkFueN&
         scope=openid%20profile%20email
    

    When the user gets there, your OAuth provider will show a page with your app’s name and logo, the scope of the request, an Allow button, and a Decline button.

    Depending on the implementation, the user will also hit an authentication layer, in which they will log in with username-password, social media, biometrics, or whatever other options your OAuth provider allows.

  3. Your Oauth provider will redirect the user to your redirect_uri along with some parameters:

    parameter value purpose
    code the authorization code from your OAuth provider proof that the user gave consent
    state the same random string you gave in the previous step used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://www.piedpiper.com/login?
         code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg&
         state=E7bMSVO7DlxkFueN
    

    Verify that state is the same as the one you sent; if it’s not, someone other than your OAuth provider is trying to execute a CSRF attack. Save code for the next step; you’ll need it to get an access token.

  4. Make an HTTP request to your OAuth provider’s token endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    code_verifier the random string generated in step 1 will be compared with the code_challenge you sent in step 2, which allows your OAuth provider to authenticate your app
    code the authorization code from your OAuth provider proof that the user gave consent
    grant_type authorization_code indicates that you’re using an authorization code as a grant
    redirect_uri the same redirect URI as you gave in step 2 used by the OAuth provider to verify your token request

    Example POST request:

     POST /oauth2/token HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     code_verifier=zfLfZKs05SjlQOJv&
     code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg&
     grant_type=authorization_code&
     redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin
    
  5. Your OAuth provider will respond with JSON, including the access_token:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    refresh_token (optional) an opaque token used to get new access_tokens after they expire

    Example JSON:

     {
         "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw",
         "expires_in" : 3600,
         "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ"
     }
    

    That’s the end of the Authentication Grant Flow +PKCE! See Using Access Tokens for what to do next.

5.3. Device Flow

Not sure if this is the right flow for your app?

  1. Make an HTTP request to your OAuth provider’s device_authorize endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    scope (optional) space-separated list of access levels tells your OAuth provider which specific data you’re trying to get

    Example POST request:

     POST /oauth2/device_authorize HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     scope=openid%20profile%20email
    
  2. Your OAuth provider will respond with JSON including the following parameters:

    parameter value purpose
    device_code a random string used by your OAuth provider to identify the user’s device
    expires_in integer number of seconds number of seconds the user has before user_code expires
    interval integer number of seconds number of seconds your app should wait between token endpoint requests
    user_code a random string short code for the user to type in after navigating to verification_uri
    verification_uri URI pointing to your OAuth provider’s site short web address for the user to type in a browser

    Example JSON:

     {
         "device_code": "e6f_lF1rG_yroI0DxeQB5OrLDKU18lrDhFXeQqIKAjg",
         "verification_uri": "https://oauth-provider.com/device",
         "user_code": "FBGLLF",
         "expires_in": 600,
         "interval": 5
     }
    

    Display verification_uri and user_code on the device. The user will type the URI in another browser and arrive at a page on your OAuth provider’s site. Your OAuth provider will show a page with your app’s name and logo, the scope of the request, and a form to enter the user_code. (Entering the code is basically the same as clicking Allow in Authorization Code Flow.)

    Depending on the implementation, the user will also hit an authentication layer, in which they will log in with username-password, social media, biometrics, or whatever other options your OAuth provider allows.

  3. Make HTTP requests to your OAuth provider’s token endpoint. Because there’s no way to know if the user has finished entering the user_code, you’ll need to check repeatedly. The interval parameter from step 2 tells you how long to wait between requests.

    You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    device_code the device code from step 2 used by your OAuth provider to identify the user’s device
    grant_type device_code indicates that you’re using a device code as a grant

    Example POST request:

     POST /oauth2/token HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     device_code=e6f_lF1rG_yroI0DxeQB5OrLDKU18lrDhFXeQqIKAjg&
     grant_type=device_code
    

    If the user hasn’t typed in their code, yet, your OAuth provider will respond with an authorization_pending error:

     {
         "error": "authorization_pending"
     }
    
  4. Once the user has finished authorizing, your OAuth provider will respond with JSON, including the access_token:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    refresh_token (optional) an opaque token used to get new access_tokens after they expire

    Example JSON:

     {
         "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw",
         "expires_in" : 3600,
         "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ"
     }
    

    That’s the end of the Device Flow! See Using Access Tokens for what to do next.

5.4. Client Credentials Flow

Not sure if this is the right flow for your app?

  1. Make an HTTP request to your OAuth provider’s token endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    client_secret your client secret used by your OAuth provider to authenticate your app
    grant_type client_credentials indicates that you’re using client credentials as a grant
    scope (optional) space-separated list of access levels tells your OAuth provider which specific data you’re trying to get

    Example POST request:

     POST /oauth2/token HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     client_secret=_UctTBl5PG89-vCwrOo0FqYLywnUC4hSjx927sLjuzM&
     grant_type=client_credentials&
     scope=openid%20profile%20email
    
  2. Your OAuth provider will respond with JSON, including the access_token:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    refresh_token (optional) an opaque token used to get new access_tokens after they expire

    Example JSON:

     {
         "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw",
         "expires_in" : 3600,
         "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ"
     }
    

    That’s the end of the Authentication Grant Flow! See Using Access Tokens for what to do next.

5.5. User Credentials Flow

Not sure if this is the right flow for your app?

  1. Get the user’s username and password. This is usually done with a form. Don’t store these credentials; use temporary variables and throw them out after authorization.

  2. Make an HTTP request to your OAuth provider’s token endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    client_secret your client secret used by your OAuth provider to authenticate your app
    grant_type password indicates that you’re using user credentials as a grant
    password password from step 1 used by the OAuth provider to authenticate the user
    username username from step 1 used by the OAuth provider to authenticate the user

    Example POST request:

     POST /oauth2/token HTTP/1.1
     Host: oauth-provider.com
    
     client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&
     client_secret=_UctTBl5PG89-vCwrOo0FqYLywnUC4hSjx927sLjuzM&
     grant_type=password&
     username=matt-boisseau&
     password=R7qq4YAx3q9S
    
  3. Your OAuth provider will respond with JSON, including the access_token:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    refresh_token (optional) an opaque token used to get new access_tokens after they expire

    Example JSON:

     {
         "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw",
         "expires_in" : 3600,
         "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ"
     }
    

    That’s the end of the User Credentials Flow! See Using Access Tokens for what to do next.

5.6. Implicit Flow

Not sure if this is the right flow for your app?

Implicit Flow was designed as a workaround for public apps (those that can’t keep a confidential secret) before the CORS was available in modern browsers.

Implicit Flow simply skips the code exchange step that would require authentication with a client secret. Instead, you ask for the access token directly in the authorization redirect. This means that the access token is exposed in the redirect URI parameters (see step 2). For this reason, Implicit Flow is not recommended; instead, you should use Authorization Code Flow +PKCE.

If you still want to use Implicit Flow, keep in mind that tokens should be as short-lived as possible. When it comes to potentially exposed tokens, less active time means less to gain for would-be attackers. Refresh tokens are long-lived by design, and are therefore too risky to expose.

  1. Redirect the user to your OAuth provider’s authorize endpoint. You’ll need to include the following parameters:

    parameter value purpose
    client_id your client ID tells your OAuth provider which app is making a request
    redirect_uri your redirect URI tells your OAuth provider where to redirect users after authorization is done
    response_type token indicates that you’re expecting an access token in response
    scope (optional) space-separated list of access levels tells your OAuth provider which specific data you’re trying to get
    state random string used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://oauth-provider.com/oauth2/authorize?
         client_id=06494b74-a796-4723-af44-1bdb96b48875&
         redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin&
         response_type=token&
         state=E7bMSVO7DlxkFueN&
         scope=openid%20profile%20email
    

    When the user gets there, your OAuth provider will show a page with your app’s name and logo, the scope of the request, an Allow button, and a Decline button.

    Depending on the implementation, the user will also hit an authentication layer, in which they will log in with username-password, social media, biometrics, or whatever other options your OAuth provider allows.

  2. Your Oauth provider will redirect the user to your redirect_uri along with some parameters:

    parameter value purpose
    access_token a JWT or opaque token used to make API calls and access the user’s data
    expires_in integer number of seconds number of seconds the app has before access_token expires
    state random string used to verify the response from your OAuth provider and prevent CSRF attacks

    Example URI:

     https://www.piedpiper.com/login?
         access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw&
         expires_in=600&
         state=E7bMSVO7DlxkFueN
    

    Verify that state is the same as the one you sent; if it’s not, someone other than your OAuth provider is trying to execute a CSRF attack.

    That’s the end of the Implicit Flow! See Using Access Tokens for what to do next.

6. Using Access Tokens

After successfully using one of the above authorization grant flows, you should have an access token, which is either an opaque token or a JWT.

6.1. Opaque Tokens

An opaque token is really just a randomly generated string. It doesn’t contain any info on its own; instead, it works only as an API key. Include an access token with your requests to the resource server; the resource server will use the token to validate the request.

Example opaque token:

ppRbXeycI6Wu0K9WzbgGQadUoS7P

6.2. JWTs

A JWT contains base64-encoded JSON data, which can be retrieved by manually decoding or passing the JWT back to your OAuth provider’s introspect endpoint. The JSON payload might include data like the user’s roles, which authentication type they used, their email address, etc. A JWT can also be included with API requests, just like an opaque token.

Example encoded JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTc3ODYyMDAwLCJleHAiOjE1Nzc4NjIwMDAsInJvbGVzIjpbIm1vZGVyYXRvciIsInVzZXIiXX0.oinbs9H_CzT7Bl79ZL_tHLmfE53YyTlUFv284il_YCw

A JWT is separated into three parts with .s. Each part is base64-encoded.

The first chunk is the header, which tells us basic info about the JWT, like the algorithm used to hash it, and its filetype.

Example decoded JWT header:

{
	"alg": "HS256",
	"typ": "JWT"
}

The second chunk is the payload, which might include data like the user’s roles, which authentication type they used, their email address, etc.

Example decoded JWT payload:

{
	"exp": 1577862000,
	"iat": 1577862000,
	"name": "John Doe",
	"roles": ["moderator", "user"],
	"sub": "1234567890"
}

A JWT also has a digital signature, which means that the data within can be verified and trusted. They can be signed using a shared secret (known to both your OAuth provider and the resource server or a public/private key pair (with private kept by the OAuth provider to ensure it stays confidential). In the example below, oF0125yWqU*^ is the shared secret.

Example decoded JWT signature:

HMACSHA256(
	base64UrlEncode(header) + "." +
	base64UrlEncode(payload),
	oF0125yWqU*^
)

7. OAuth Jargon Glossary

OAuth term definition in plain English
access token short-lived pass used to access user’s data from the resource server
authentication (authN) the act of verifying a user’s or an app’s identity
authorization (authZ) the act of giving consent to an app
authorization grant affirmation from OAuth provider that user gave consent
authorization request redirection of user to OAuth provider’s “Authorize” endpoint
authorization server (AS) OAuth provider
client app that wants to access user’s data
client ID app’s public username, used by OAuth provider to identify the app
client secret app’s private password, used by OAuth provider to authenticate the app
code verifier random, hashed string used in place of a secret when it’s not possible to keep a secret
protected resource user’s data
redirect URL location on the app where the user lands after authorization
refresh token long-lived pass used to get new access tokens
resource owner (RO) user who owns data
resource server (RS) API used to access user’s data
state random string sent with authorization request and returned with authorization code, used to verify the transaction
token exchange exchange of client id/secret and authorization code for access token