OAuth Authorization Code Grant

On this page

The Authorization Code grant is the recommended grant for logging users in. Your app redirects the browser to FusionAuth, and FusionAuth logs the user in. FusionAuth then redirects the browser back to your app with an authorization code. Your app exchanges the code for tokens.

FusionAuthYour AppBrowserFusionAuthYour AppBrowserClick log inRedirect to /oauth2/authorize with the code challengeRequest the login pageRedirect to the redirect URI with the authorization codeRequest the redirect URIPOST /oauth2/token with the code and the code verifierReturn the tokens

Requests between the browser, your app, and FusionAuth during an Authorization Code grant.

Before you start, configure the Application, as described in Configure Application OAuth Settings. Most apps use an OAuth library for these requests. To configure a library, see Add Login to Your App. The following example shows the raw requests. The example uses a FusionAuth instance at https://piedpiper.fusionauth.io and an app at https://www.piedpiper.com.

Proof Key for Code Exchange#

Use Proof Key for Code Exchange (PKCE, pronounced "pixie") with every Authorization Code grant. PKCE stops an attacker who intercepts the authorization code from exchanging the code for tokens.

Before each login, your app generates a random string called the code verifier. Your app hashes the code verifier with SHA-256 and sends the result to the authorize endpoint as the code_challenge parameter. FusionAuth stores the code challenge with the authorization code. When your app exchanges the code at the token endpoint, your app sends the original code_verifier. FusionAuth hashes the code verifier and rejects the request when the hash doesn't match the code challenge. An attacker who steals the code doesn't have the code verifier.

PKCE replaces the client secret for single-page and mobile apps, because a browser or a device can't keep a secret. Server-side apps send both. Most OAuth libraries generate the code verifier and the code challenge for you. To require PKCE for an Application, set PKCE to Required on the OAuth tab of the Application.

The following example uses the code verifier dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk and the code challenge E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM.

Send the Browser to the Authorize Endpoint#

To start a login, redirect the browser to the /oauth2/authorize endpoint. The example adds line breaks for readability.

Example Authorize URL
https://piedpiper.fusionauth.io/oauth2/authorize?
    client_id=06494b74-a796-4723-af44-1bdb96b48875
    &code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
    &code_challenge_method=S256
    &redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Fcallback
    &response_type=code
    &scope=openid%20offline_access
    &state=abc123
    &tenantId=78dda1c8-14d4-4c98-be75-0ccef244297d

The URL uses the following parameters:

  • client_id is the Client Id from the OAuth tab of the Application.
  • code_challenge and code_challenge_method carry the PKCE code challenge.
  • redirect_uri is the URL that FusionAuth redirects the browser to after login. Add the URL to Authorized redirect URLs on the OAuth tab of the Application.
  • response_type is always code for this grant.
  • scope requests an Id TokenA JSON Web Token that contains information about the authenticated user. with openid and a Refresh TokenA credential that an application exchanges for a new access token without asking the user to log in again. with offline_access.
  • state is a random value that your app stores before the redirect. Your app compares the value on the way back to block cross-site request forgery.
  • tenantId is the Id of the Tenant. FusionAuth uses the Tenant to pick the theme for the login page.

For every parameter, see the Authorize endpoint.

Handle the Redirect#

After the user logs in, FusionAuth redirects the browser to the redirect_uri. The code parameter holds the authorization code:

Example HTTP Redirect Response
HTTP/1.1 302 Found
Location: https://www.piedpiper.com/callback?
    code=pU2DHOWjSCVh6NJKi1ClhBYNKfuqbZVT
    &iss=https%3A%2F%2Fpiedpiper.fusionauth.io
    &locale=en
    &state=abc123
    &userState=Authenticated

Before you use the code, check that state matches the value your app stored. Check that iss matches the Tenant Issuer, as described in Issuer Validation. For the other parameters, see Redirect Parameters.

Exchange the Code for Tokens#

Your app's server sends the code to the /oauth2/token endpoint. The client_secret authenticates your app, and the code_verifier completes PKCE. The example adds line breaks for readability.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded

client_id=06494b74-a796-4723-af44-1bdb96b48875
    &client_secret=example-client-secret-do-not-use
    &code=pU2DHOWjSCVh6NJKi1ClhBYNKfuqbZVT
    &code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
    &grant_type=authorization_code
    &redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Fcallback

You can send the Client Id and the client secret in an Authorization: Basic header instead of the body. A single-page or mobile app leaves out client_secret. Set Client Authentication on the OAuth tab of the Application to Not required when using PKCE, and FusionAuth accepts the code verifier in place of the secret. A single-page app without a backend can use the hosted backend to make this request instead.

Replace the example client secret

example-client-secret-do-not-use is a placeholder. Don't use the placeholder in a real Application. Use the Client secret from the OAuth tab of your Application, and keep the secret on your server.

FusionAuth returns the tokens:

Example JSON Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ",
  "refresh_token_id": "4c8927f1-95cd-4bf8-9534-066389ffff5e",
  "scope": "openid offline_access",
  "token_type": "Bearer",
  "userId": "3b6d2f70-4821-4694-ac89-60333c9c4165"
}

For every request and response field, see the Token endpoint.

Check the User's Access#

Validate the access token before your app trusts the claims. Most OAuth libraries validate tokens for you. For the claims in each token, see Tokens.

The roles claim lists the user's roles in the Application. FusionAuth adds the roles claim only when the user has a RegistrationThe association between a User and an Application. for the Application. When the user has no Registration, your app can reject the login or start a registration flow.

When your app needs more user data than the claims hold, send the access token to the UserInfo endpoint. With an API key, your app can retrieve the full User with the Retrieve a User API. The sub claim holds the User Id.

After the checks pass, log the user in to your app. A web app usually creates a session and stores the user in the session.

Log Out#

To log the user out, end the session in your app first. Then redirect the browser to the /oauth2/logout endpoint, which ends the FusionAuth session. The example adds line breaks for readability.

Example HTTP Request
GET /oauth2/logout?
      client_id=06494b74-a796-4723-af44-1bdb96b48875
      &tenantId=78dda1c8-14d4-4c98-be75-0ccef244297d HTTP/1.1
Host: piedpiper.fusionauth.io

FusionAuth redirects the browser to the Logout URL of the Application:

Example HTTP Response
HTTP/1.1 302 Found
Location: https://www.piedpiper.com

For every parameter, see the Logout endpoint.