OAuth 2.0 & OpenID Connect

1. Overview

FusionAuth supports the following grant types as defined by the OAuth 2.0 framework in RFC 6749, RFC 8628, and OpenID Connect Core.

  • Authorization Code Grant

  • Implicit Grant

  • Password Grant (also referred to as the Resource Owner Credentials Grant)

  • Refresh Token Grant

  • Device Authorization Grant

 

To being using the FusionAuth login system, start by configuring your Application for OAuth2.

 

It is recommended to utilize the Authorization Code Grant unless you have a technical requirement that makes a different grant a better choice. The following outline some example login flows.

2. Configure Application OAuth Settings

Navigate to the Application configuration from the main menu, Applications.

If you have already created a FusionAuth Application for the purpose your application, you do not need to create another, however you will still need to complete the OAuth configuration. If an application has not yet been created, click Add and name your application accordingly and fill out the OAuth configuration.

In this example you will see we have created a new Application named Pied Piper and have filled out the fields in the OAuth Configuration tab. A FusionAuth application represents an authenticated resource that you will be using with FusionAuth.

Additional OAuth controls can be managed through Tenant Configuration.

Application OAuth Configuration
Table 1. OAuth Form Fields

Client Id Read-Only

The unique client identifier as defined by RFC 6749 Section 2.2. This value is read only, it is equal to unique Id of the Application.

Client secret Read-Only

The client secret as defined by RFC 6749 Section 2.3-1.

Available Since 1.3.0 This value may be optionally re-generated by clicking the regenerate button. If this Application is configured to require client authentication, changing the client secret will cause all clients to fail client authentication and they will not be able to complete the OAuth login process. If this Application is not configured to require client authentication, regenerating this secret will not have any external effect.

Require authentication Available Since 1.3.0

When enabled, client authentication will be enforced for this Application. This means that the Token endpoint will require Basic Authentication to access the endpoint.

See the Authorization options for the Token endpoint.

Generate refresh tokens Available Since 1.3.0

When enabled, a refresh token will be generated when the offline_access scope has been requested and other required values have been provided.

In order to use the Refresh Token with the Refresh Grant to refresh a token, you must ensure that the Refresh Token grant is enabled. See the Enabled grants parameter.

Authorized redirect URLs Optional

One or more authorized URLs that the OAuth grant workflow may redirect to upon completion.

Authorized request origin URLs Optional

One or more authorized origins that can initiate the OAuth grant to the /oauth2/authorize or /oauth2/token endpoints. Leaving this value empty will allow all origins.

Logout URL Optional

The URL used to perform the 302 redirect as the response from the /oauth2/logout API. If this value is omitted, the global configuration value will be used. See the Logout URL under the OAuth tab of the System Settings.

Logout behavior Required

The behavior to follow upon call to /oauth2/logout.

Enabled grants

The OAuth grants enabled for this Application. When creating a new Application, the Authorization Code and Refresh Token grants will be enabled by default.

Device Verification URL Optional

The URL to direct the end-user to for the user-interaction portion of the Device Authorization Grant. This field is required if Device is enabled in the OAuth Enabled grants for this Application.

3. Example Authorization Code Grant

Mobile applications require additional security in implementing the Authorization Code Grant Flow due to inability to safely store a client-secret and the potential of the authorization code being intercepted. For these reasons, it is best practice to implement the Authorization Code Grant Flow with Proof Key for Code Exchange (PKCE, pronounced "pixie").

With PKCE, a unique code_verifier is generated by the client and transformed using a code_challenge_method to derive a code_challenge. The code_challenge and code_challenge_method are added to the initial front-channel /oauth2/authorize request. FusionAuth stores the code_challenge and code_challenge_method, and does not return this information in the response. The client sends the code_verifier in the subsequent back-channel request to /oauth2/token. FusionAuth transforms the code_verifier using the stored code_challenge_method and compares the result to the stored code_challenge to verify authenticity. FusionAuth supports S256 as a code_challenge_method.

Please refer to RFC 7636 for PKCE specifications. Review the Authorization and Token endpoint documentation for more detail.

3.1. Point your application to the authorize endpoint

Now that your FusionAuth application has been configured to use the OAuth provider, you may now point the login for your application to the FusionAuth authorize endpoint which will now handle user authentication.

For the purposes of this example, I will make the assumption that FusionAuth App is running locally at http://localhost:9011, the client_id will be found on the OAuth tab in the application configuration, the redirect_uri will be where you wish FusionAuth to redirect the browser when the authorization step has completed. This value will need to be pre-defined in the authorized redirect URLs in the OAuth configuration. The response_type will always be code for this grant type. The tenantId will be the unique Id of the tenant this request is scoped for, the tenant’s configured theme will be applied.

Review the Authorization endpoint documentation for more detail.

http://localhost:9011/oauth2/authorize?client_id=06494b74-a796-4723-af44-1bdb96b48875&redirect_uri=https://www.piedpiper.com/login&response_type=code&tenantId=78dda1c8-14d4-4c98-be75-0ccef244297d

3.2. Consume the authorization code returned from the authorize request

When the authorize request completes successfully it will respond with a status code of 302 to the location provided by the redirect_uri parameter. The request will contain a code parameter which can be exchanged for an access token. The access token contains the user Id of the authenticated user which can then be used to retrieve the entire user object.

Review the Token endpoint documentation for more detail. The following is an example redirect URI containing the authorization code.

https://www.piedpiper.com/login?code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg=&userState=Authenticated

3.3. Exchange the authorization code for an access token

The last step to complete the authentication process and retrieve the users Id is to exchange the returned authorization code for an access token. The JSON response will contain the user Id of the authenticated user.

If the authorization code grant is being implemented in a Single Page App (SPA), the token request should be made by the application server in order to keep the client secret secure from introspection of the client code.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 436
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&code=+WYT3XemV4f81ghHi4V+RyNwvATDaD4FIj0BpfFC4Wzg&grant_type=authorization_code&redirect_uri=https%3A%2F%2Fwww.piedpiper.com%2Flogin
Example HTTP Response
{
  "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "expires_in" : 3600,
  "token_type" : "Bearer",
  "userId" : "3b6d2f70-4821-4694-ac89-60333c9c4165"
}

3.4. Verify Authorization

If you only need to validate registration and User roles, this can be done by inspecting the JWT payload as returned in the access_token property of the response body.

If you require the entire User object to validate authorization, you may need to retrieve the entire User. The User may be retrieved in one of several ways. If you have an API key ou can retrieve the User by Id or email, these two values are returned in the JWT payload. The email address is returned in the email identity claim, and the User’s Id is returned in the sub identity claim. You may also retrieve the User without an API key by utilizing the JWT as returned in the access_token property in the response body.

See the Retrieve a User API for examples.

You may also choose to use the Introspect or Userinfo endpoints to validate the access token returned from the Token endpoint and to provided you decode claims as a JSON object.

Now that you have the user, or retrieved the roles from the JWT, you may review their roles and registration to ensure they have adequate authority for the intended action, and if the user is not yet registered for the requested application, you can either fail their login, or complete a registration workflow. Once you have determined a user can be logged into your application, you’ll need to log them into your application. For a web based application, this generally will include creating an HTTP session and storing the user in the newly created session.

3.5. Log Out

To log the user out, a typical workflow would include first logging out of your application, if that is successful, you would then log the user out of FusionAuth. This is accomplished by making a [GET] request to the /oauth2/logout endpoint. The logout request will complete with a 302 redirect to the configured logout URL.

[GET] http://localhost:9011/oauth2/logout?client_id=06494b74-a796-4723-af44-1bdb96b48875&tenantId=78dda1c8-14d4-4c98-be75-0ccef244297d

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

4. Example Implicit Grant

The Implicit Grant is similar to the Authorization grant, instead of exchanging a code for an access token, the token is provided on the initial request. The Authorization Grant is preferred in almost all cases over the Implicit Grant due to potential security risks, however the Implicit Grant is an option for static Single Page Apps.

4.1. Exchange the user credentials for an access token

Once you have collected the user’s email and password you will make a GET request to the Authorize endpoint. Below is an example HTTP request where the user’s email is richard@piedpiper.com and password is disrupt. The response_type will always be token.

Example HTTP Request
GET /oauth2/authorize HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 436
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&response_type=token&username=richard%40piedpiper.com&password=disrupt

 

Upon successful authentication, a redirect to the configured redirect_uri will be made an access_token as one of the redirect parameters. The following is an example HTTP 302 redirect, line breaks added to improve readability. The redirect from an Implicit Grant will contain parameters after the fragment #.

HTTP Redirect Response
HTTP/1.1 302 Found
Location: https://piedpiper.com/callback#
           access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo
           &expires_in=3599
           &locale=fr
           &token_type=Bearer
           &userState=Authenticated

 

5. Example Resource Owner Password Credentials Grant

The Resource Owner Password Credentials Grant (A.K.A. Password Grant) allows you to obtain both an access token and a refresh token by specifying the offline_access scope.

5.1. Exchange the user credentials for an access token

Once you have collected the user’s email and password you will make a POST request to the Token endpoint. Below is an example HTTP request where the user’s email is richard@piedpiper.com and password is disrupt. The grant_type will always be password.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 436
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&grant_type=password&username=richard%40piedpiper.com&password=disrupt&scopes=offline_access
Example HTTP Response
{
  "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "expires_in" : 3600,
  "refresh_token": "Nu00yJrGw0qlBJNUz2S6LJ3KZFN7uw6Dj4C2mnzF4I6wkM5xingxuw",
  "token_type" : "Bearer",
  "userId" : "3b6d2f70-4821-4694-ac89-60333c9c4165"
}

6. Example Refresh Token Grant

An access token is designed to have a short time-to-live (TTL). A related refresh token with a longer TTL can be used for generating new access tokens and extending a user’s session. The application’s OAuth settings must be configured with "Generate refresh tokens" enabled, and "Refresh Token" enabled in as an "Enabled grant".

6.1. Exchange a refresh token for an access token

With a refresh token obtained from a previous call to the /Authorize endpoint, a new access token may be generated with a POST request to the Token endpoint. Below is an example HTTP request, the grant_type will always be refresh_token.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 436
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&grant_type=refresh_token&refresh_token=Nu00yJrGw0qlBJNUz2S6LJ3KZFN7uw6Dj4C2mnzF4I6wkM5xingxuw
Example HTTP Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImVjZWUzMTYyZjAifQ.eyJhdWQiOiI4YmY4YWIwYy1iMWNlLTQ0NjUtYmQzNy1jMTU1MThjYWU2YmQiLCJleHAiOjE1NzA0ODQwNTcsImlhdCI6MTU3MDQ4MDQ1NywiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiJhZjRiMzk2Yy01MGM4LTQwNzQtOTA5YS0zYzgwNjU0OTEzMzUiLCJhdXRoZW50aWNhdGlvblR5cGUiOiJSRUZSRVNIX1RPS0VOIiwiZW1haWwiOiJqb2huQGRvZS5pbyIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqb2hubnkxMjMiLCJyb2xlcyI6WyJjb21tdW5pdHlfaGVscGVyIiwidXNlciJdLCJhcHBsaWNhdGlvbklkIjoiOGJmOGFiMGMtYjFjZS00NDY1LWJkMzctYzE1NTE4Y2FlNmJkIn0.laSlkKQMOwZmfI_3NT3-1F_VdpLL-ceCQZ2fRL1lvF4",
  "expires_in": 3600,
  "scope": "offline_access",
  "token_type": "Bearer",
  "userId": "3b6d2f70-4821-4694-ac89-60333c9c4165"
}

7. Example Device Authorization Grant

This example contains screenshots of our Device Grant Example which may be a useful code reference during implementation.

7.1. Device Authorization Grant Configuration

In order to leverage FusionAuth for the Device Authorization Grant, the Device Grant must be enabled and the Device Verification URL must be set. See the Configure Application OAuth Settings section above.

FusionAuth requires that the Device Verification URL be a page that you control within your application so that a required Tenant Id is provided throughout the grant flow. While you may host your own form on this page, FusionAuth provides a themed OAuth device template that may be redirected to from your application to complete the user-interaction portion of the Device Authorization Grant as a convenience. This template is located at /oauth2/device. With the required request parameters being client_id and tenantId. On submit of the OAuth device template the end-user is prompted to authenticate using the Authorization Grant flow. This will redirect to the configured OAuth redirect_uri per the typical Authorization Grant flow. The Device Authorization Grant will be considered approved when the Authorization Grant code has been exchanged for a token.

Default values are provided for the durations that the device code and user code remain valid, as well as the user code generator settings. These values may be adjusted through the "Advanced" tab of Tenant Configuration.

7.2. Initiate the Device Authorization Grant flow

In order to initiate the Device Authorization Grant flow, make a request from the device to the Device Authorize endpoint, which is also discoverable via the OpenID Configuration. This request may be made with the optional scope field with a value of offline_access if you would like a refresh token provided on the eventual /oauth2/token endpoint return. This request will return a JSON response with values necessary to fulfill the remainder of the grant flow.

OAuth Device Example - Connect
Example HTTP Request
POST /oauth2/device_authorize HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 67
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&scope=offline_access
Example JSON Response
{
  "device_code": "e6f_lF1rG_yroI0DxeQB5OrLDKU18lrDhFXeQqIKAjg",
  "expires_in": 600,
  "interval": 5,
  "user_code": "SFYNPV",
  "verification_uri": "http://localhost:9011/oauth2/device",
  "verification_uri_complete": "http://localhost:9011/oauth2/device?user_code=SFYNPV"
}

7.3. Poll Token endpoint

Upon receiving a response from the Device Authorize endpoint the device may begin polling the Token endpoint with the device_code at the requested interval in seconds returned in the response. Requests to the Token endpoint will return an error stating that authorization is pending, until the end-user approves the request, at which point an access token will be returned.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 166
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&device_code=e6f_lF1rG_yroI0DxeQB5OrLDKU18lrDhFXeQqIKAjg&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
Example pending JSON Error Response
{
  "error": "authorization_pending",
  "error_description": "The authorization request is still pending"
}
Example expired JSON Error Response
{
  "error": "expired_token",
  "error_description": "The device_code has expired, and the device authorization session has concluded."
}
Example invalid JSON Error Response
{
  "error": "invalid_request",
  "error_reason": "invalid_device_code",
  "error_description": "The request has an invalid parameter: device_code"
}

7.4. User-interaction

Upon receiving a response from the Device Authorize endpoint the device may display to the end-user the user_code and a prompt to navigate to the verification_uri. The verification_uri_complete is provided as a convenience so that the device may display a QR code used to navigate the end-user to the user-interaction page with a pre-populated user_code in the form.

OAuth Device Example - Display Code

The user should then navigate to the displayed URL, and enter the activation code.

OAuth Device Example - User Interaction

7.5. Pass user_code to FusionAuth

Once the user_code has been received from the end-user, it may be validated by making a request to the Device Validate endpoint. This endpoint will return a 200 response code without a JSON body on successful validation.

Upon validating the end-user provided user_code the typical Authorization Grant, Implicit Grant, or Password Grant flows may be followed for authentication. The OAuth endpoints that facilitate these typical OAuth flows take a user_code parameter to facilitate the Device Authorization Grant approval. See the Authorize endpoint and Token endpoint documentation for more information.

OAuth Device Example - Success

7.6. Receive access_token

Once the user has provided a valid user_code and successfully authenticated, the request from the device to the Token endpoint will return successfully with an access token.

Example HTTP Request
POST /oauth2/token HTTP/1.1
Host: piedpiper.fusionauth.io
Content-Type: application/x-www-form-urlencoded
Accept: */*
Content-Length: 166
client_id=3c219e58-ed0e-4b18-ad48-f4f92793ae32&device_code=e6f_lF1rG_yroI0DxeQB5OrLDKU18lrDhFXeQqIKAjg&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code
Example JSON Response
{
  "access_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "expires_in" : 3600,
  "id_token" : "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODUxNDA5ODQsImlhdCI6MTQ4NTEzNzM4NCwiaXNzIjoiYWNtZS5jb20iLCJzdWIiOiIyOWFjMGMxOC0wYjRhLTQyY2YtODJmYy0wM2Q1NzAzMThhMWQiLCJhcHBsaWNhdGlvbklkIjoiNzkxMDM3MzQtOTdhYi00ZDFhLWFmMzctZTAwNmQwNWQyOTUyIiwicm9sZXMiOltdfQ.Mp0Pcwsz5VECK11Kf2ZZNF_SMKu5CgBeLN9ZOP04kZo",
  "refresh_token": "ze9fi6Y9sMSf3yWp3aaO2w7AMav2MFdiMIi2GObrAi-i3248oo0jTQ",
  "token_type" : "Bearer",
  "userId" : "3b6d2f70-4821-4694-ac89-60333c9c4165"
}