Overview
The Authorization Code Flow is the most commonly used and recommended OAuth 2.0 authentication flow for server-side and web applications. It provides a secure method for obtaining access tokens by exchanging an authorization code obtained through a user's interaction with an authorization server.
If the application can be trusted to hold a secret (like a web application with secure backend), the standard Authorization Code flow can be used. If the application cannot securely hold a secret (like a web single-page app or mobile app), and particularly if access tokens are needed, it should utilize the Proof Key Code Exchange feature to prevent token hijacking.
If only ID tokens are needed, an alternate login flow (Implicit Flow with Form post) may also be used.
Flow Description
- Client Registration: The client application must be registered via the UserClouds Developer Console. During registration, a unique client ID is configured.
- Authorization Request: The client initiates the flow by redirecting the user's browser to UserClouds's authorize endpoint.
- User Authentication and Consent: The user is prompted to authenticate with the UserClouds authorization server and (if necessary) consent to the requested scopes. After successful authentication and consent, the authorization server generates an authorization code.
- Code / Token Exchange:
- UserClouds redirects the user's browser back to the client's redirect URI. The client extracts the authorization code from the query parameters and sends an HTTP
POST
request to UserClouds's token endpoint. - In response, UserClouds provides an access token and (possibly) a refresh token. The client can use the access token to access protect resources on behalf of the user.
- UserClouds redirects the user's browser back to the client's redirect URI. The client extracts the authorization code from the query parameters and sends an HTTP
- Token Refresh (Optional): If a refresh token is obtained during Step 4, the client can use it to request new access tokens without requiring user interaction. This allows the client to maintain user sessions and continue accessing resources after the original token expires.
Step 2 - Authorization Request
Request Example
import "golang.org/x/oauth2"
prov, _ := oidc.NewProvider(context.Background(), "https://sample.tenant.userclouds.com")
oauthConfig := oauth2.Config{
ClientID: "5f107e226353791560f93164a09f7e0f",
Endpoint: prov.Endpoint(),
RedirectURL: "https://contoso.com/callback",
Scopes: []string{"openid <ADDITIONAL_SCOPES>"},
}
Without PKCE
GET /oidc/authorize?response_type=code&client_id=5f107e226353791560f93164a09f7e0f&redirect_uri=https%3A%2F%contoso.com%2Fcallback&scope=openid+profile+email&state=n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk HTTP/1.1
Host: sample.tenant.userclouds.com
curl 'https://sample.tenant.userclouds.com/oidc/authorize?response_type=code&client_id=5f107e226353791560f93164a09f7e0f&redirect_uri=https%3A%2F%contoso.com%2Fcallback&&scope=openid+profile+email&state=n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk'
// Authorization Code Flow without Proof Key Code Exchange (PKCE)
url := oauthConfig.AuthCodeURL("n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk")
// Redirect user agent to URL authorize endpoint
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
With PKCE
GET /oidc/authorize?response_type=code&client_id=5f107e226353791560f93164a09f7e0f&redirect_uri=https%3A%2F%contoso.com%2Fcallback&scope=openid+profile+email&state=n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk&code_challenge=fJy4Nvl38sFmKyYUMZC1klsg9kn5HKXDUHEdeIuZnyc&code_challenge_method=S256 HTTP/1.1
Host: sample.tenant.userclouds.com
curl 'https://sample.tenant.userclouds.com/oidc/authorize?response_type=code&client_id=5f107e226353791560f93164a09f7e0f&redirect_uri=https%3A%2F%contoso.com%2Fcallback&&scope=openid+profile+email&state=n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk&code_challenge=fJy4Nvl38sFmKyYUMZC1klsg9kn5HKXDUHEdeIuZnyc&code_challenge_method=S256'
// Authorization Code Flow with Proof Key Code Exchange (PKCE) using SHA256 (the only supported method)
url := oauthConfig.AuthCodeURL(
state,
oauth.SetAuthURLParam("code_challenge_method", "S256"),
oauth.SetAuthURLParam("code_challenge", "<CODE_CHALLENGE>"))
// Redirect user agent to URL authorize endpoint
http.Redirect(w, r, url, http.StatusTemporaryRedirect)
Because this is a GET
request, all parameters should be specified as query parameters in the URL.
Parameter | Type | Required? | Description |
---|---|---|---|
response_type | string | yes | code |
client_id | string | yes | The Client ID of a registered app/client in your UserClouds tenant. |
redirect_uri | string | yes | A URL to your application where the user agent will be redirected on completion. Must use https\:// for production web apps, or a mobile custom scheme for mobile apps. Can use http\:// for localhost and development tenants. |
scope | string | yes | Must specify openid in order to get an ID token from the token exchange endpoint later. May also specify profile , email , or other common scopes (see OIDC Spec for details). |
state | string | yes | An application-defined value used for 2 purpose: encode application state/context for a request, and to prevent CSRF attacks. |
code_challenge_method | string | no | Indicates that PKCE should be used with a given method. Only 'S256' (SHA 256) is currently supported. |
code_challenge | string | no | A value generated by applying code_challenge_method (i.e. SHA 256) to a code_verifier value generated by the client. See the PKCE spec for more details. NOTE: this value is required iff code_challenge_method is specified. |
Response Example
HTTP/1.1 302 Found
Location: https://contoso.com/callback?code=VieHyGShFqT57%2FjSeqTxQQEraGKhkYK0IMIyFzRRziQKy6G7eMPXDy0s8AiWSwNy&state=n9ftgCrrLNQ7sfxnnFmNcabEn8hFvAypP6Hu625WtBk
HTTP/1.1 302 Found
Location: https://contoso.com/callback?error=invalid_grant&error_description=The+credentials+were+invalid
If the request is valid, the immediate GET
call to the authorize endpoint will redirect to Plex's internal endpoint(s) for the user to perform an interactive login. After the user sucessfully authenticates (or in case of an error in the process), the user agent will ultimately be redirected (HTTP 302) to the application-provided (and pre-registered) redirect URL, unless the redirect URL and/or Client ID are invalid (in which case the response will be a 400 Bad Request
).
Parameter | Type | Condition | Description |
---|---|---|---|
code | string | success | Cryptographically secure token which can be exchanged with the Token endpoint. |
state | string | success | The application-defined value passed in to the Authorize call. The client should validate that this matches to prevent attacks (hijacks, replays, etc.). |
error | string | failure | One of the OAuth/OIDC-spec defined error values. See the OAuth 2.0 spec for more details. |
error_description | string | failure | A human readable explanation of the error. |
Step 4 - Code / Token Exchange
Token Exchange is a key step within the OAuth 2.0 Authorization Code Flow, where the client application exchanges an authorization code for access tokens and optionally a refresh token. To exchange an authorization code for an access and/or ID token, use this code:
POST /oidc/token HTTP/1.1
Host: sample.tenant.userclouds.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&client_id=5f107e226353791560f93164a09f7e0f&client_secret=iStpcFf3gfiUjnsbWGFY0C9aWkPzDqT14eyp23ysKuI6iBbOAWcode=VieHyGShFqT57%2FjSeqTxQQEraGKhkYK0IMIyFzRRziQKy6G7eMPXDy0s8AiWSwNy&redirect_uri=https%3A%2F%contoso.com%2Fcallback&code_verifier=iyMU3Af48ZZSPCbJGSxaUGmUJa-6uGiyTq5dwOvuvpg
curl --X POST \
-H 'content-type: application/x-www-form-urlencoded' \
-d '{"response_type":"code", "client_id":"5f107e226353791560f93164a09f7e0f", "client_secret":"iStpcFf3gfiUjnsbWGFY0C9aWkPzDqT14eyp23ysKuI6iBbOAW", "code":"VieHyGShFqT57%2FjSeqTxQQEraGKhkYK0IMIyFzRRziQKy6G7eMPXDy0s8AiWSwNy", "redirect_uri":"https://contoso.com/callback" "code_verifier":"iyMU3Af48ZZSPCbJGSxaUGmUJa-6uGiyTq5dwOvuvpg"}'
'https://sample.tenant.userclouds.com/oidc/token'
import "golang.org/x/oauth2"
prov, _ := oidc.NewProvider(context.Background(), "https://sample.tenant.userclouds.com")
oauthConfig := oauth2.Config{
ClientID: "5f107e226353791560f93164a09f7e0f",
ClientSecret: "iStpcFf3gfiUjnsbWGFY0C9aWkPzDqT14eyp23ysKuI6iBbOAW", // Only specified when NOT using PKCE
Endpoint: prov.Endpoint(),
RedirectURL: "https://contoso.com/callback",
}
// Authorization Code Flow without Proof Key Code Exchange (PKCE)
tokenResponse, err := oauthConfig.Exchange(ctx, "VieHyGShFqT57%2FjSeqTxQQEraGKhkYK0IMIyFzRRziQKy6G7eMPXDy0s8AiWSwNy")
// -- OR --
// Authorization Code Flow *with* Proof Key Code Exchange (PKCE) using SHA256 (the only supported method)
tokenResponse, err := oauthConfig.Exchange(ctx, "VieHyGShFqT57%2FjSeqTxQQEraGKhkYK0IMIyFzRRziQKy6G7eMPXDy0s8AiWSwNy", oauth.SetAuthURLParam("code_verifier", "iyMU3Af48ZZSPCbJGSxaUGmUJa-6uGiyTq5dwOvuvpg"))
if err { ... }
rawIDToken, ok := tokenResponse.Extra("id_token").(string)
if !ok { ... }
accessToken := tokenResponse.AccessToken
The application uses this endpoint to exchange the code (returned after successful authorization) for a access_token and/or id_token (depending on the authorization flow used).
Request Example
This is a POST
request with all parameters form encoded in the body.
Parameter | Type | Required? | Description |
---|---|---|---|
grant_type | string | yes | authorization_code |
client_id | string | yes | The Client ID of a registered app/client in your UserClouds tenant. |
client_secret | string | no | The Client Secret of a registered app/client in your UserClouds tenant. Note that this is used only for the non-PKCE flow. |
redirect_uri | string | yes | A URL to your application where the user agent will be redirected on completion. Must use https\:// for production web apps, or a mobile custom scheme for mobile apps. Can use http\:// for localhost and development tenants. |
code_verifier | string | yes | If PKCE was used to issue the authorization code, then this value must be specified and valid for the token exchange instead of client_secret . See the PKCE spec for more details. |
When using PKCE, code_verifier
is used instead of client_secret
. When using the regular token flow, client_secret
is used instead of code_verifier
. Do not use both.
Response Example
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token":"<ACCESS_TOKEN>",
"id_token":"<ID_TOKEN>",
"token_type":"Bearer",
}
If the request is valid, the endpoint returns a json struct with an access token and/or ID token (depending on the scopes of the original request), as well as the token type which is always "Bearer".
Parameter | Type | Condition | Description |
---|---|---|---|
access_token | string | success | Cryptographically secure token which can be used to make API calls. |
id_token | string | success | A raw three-part JWT which contains information about the resource owner / user. |
token_type | string | success | Always "Bearer" |
Step 5 - Refresh Token
Request Example
POST /oauth/token HTTP/1.1
Host: sample.tenant.userclouds.com
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=your_refresh_token&client_id=your_client_id&client_secret=your_client_secret
Response Example
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "new_access_token",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "new_refresh_token"
}
Security Considerations
- Code Exchange: Ensure secure transmission of the authorization code during exchange and use a secure channel to prevent interception.
- HTTPS Usage: Utilize HTTPS to protect sensitive information during the authorization process and token exchange.
- Code Verifier: In the case of PKCE (Proof Key for Code Exchange), implement a secure method for generating and verifying the code challenge and verifier.
- Refresh Token Lifetime: Refresh tokens may have a different lifetime compared to access tokens. Properly manage refresh token expiration.
Summary
The Authorization Code Flow is a robust and secure method for obtaining access tokens by exchanging an authorization code. It's suitable for applications where server-side communication and secure storage of credentials are possible. Adhering to security best practices, such as HTTPS usage and code exchange protection, is essential to ensure the integrity and confidentiality of the flow.