---
title: Authorization code grant with PKCE
description: /oauth2/authorize
component: pingam
version: 7.2
page_id: pingam:oidc1-guide:openid-connect-authorization-code-flow-pkce
canonical_url: https://docs.pingidentity.com/pingam/8.1/am-oauth2/oauth2-authz-grant-pkce.html
llms_txt: https://docs.pingidentity.com/pingam/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
section_ids:
  proc-auth-code-generate-pkce-oidc: Generate a code Verifier and a code challenge
  proc-auth-code-browser-pkce-oidc: Get an authorization code using a browser
  proc-auth-code-no-browser-pkce-oidc: Get an authorization code without using a browser
  proc-auth-code-token-pkce-oidc: Exchange an authorization code for an ID/access token
---

# Authorization code grant with PKCE

* Endpoints

  * [/oauth2/authorize](../oauth2-guide/oauth2-authorize-endpoint.html)

  * [/oauth2/access_token](../oauth2-guide/oauth2-access_token-endpoint.html)

  * [/oauth2/userinfo](rest-api-oidc-userinfo-endpoint.html)

The authorization code grant, when combined with the PKCE standard ([RFC 7636](https://www.rfc-editor.org/info/rfc7636)), is used when the client, usually a mobile or a JavaScript application, requires access to protected resources.

The flow is similar to the regular authorization code grant type, but the client must generate a code that will be part of the communication between the client and the OpenID provider. This code mitigates against interception attacks performed by malicious users.

Since communication between the client and the OpenID provider is not secure, clients are usually *public* so their secrets do not get compromised. Also, browser-based clients making OAuth 2.0 requests to different domains must implement Cross-Origin Resource Sharing (CORS) calls to access OAuth 2.0 resources in different domains.

The PKCE flow adds three parameters on top of those used for the Authorization code grant:

* **code_verifier** (form parameter). Contains a random string that correlates the authorization request to the token request.

* **code_challenge** (query parameter). Contains a string derived from the code verifier that is sent in the authorization request and that needs to be verified later with the code verifier.

* **code_challenge_method** (query parameter). Contains the method used to derive the code challenge.

![OpenID Connect Authorization Code Grant with PKCE Flow](https://kroki.io/plantuml/svg/eNp1VMFu2zAMPU9fQaSHbkCbtL0tKAoE3QYUWNEiW3sKECgybWu1JY-S06ZfP0qKncRZfQgsinzvkXzO5FQAP7e22ZAuSg9XF5dfz68uri7hh6UC51a9wOzXGGZVBfOQ4WCODmmNmYilTw7B5uBL7UDZDIHwb6sJHUg-1zWS0rICZ3P_Kgmh0goN17xqXx5yRDhL6caaCKuZT-a5rrT06JKMlqtdKflthYBvqmqdXmO1Adeu_qDyEcdbPqqyp1uhf0U0rLOXgCBNNpAQa39bKNAgMSU34bgfNAoh07IgWUNOtk79si48g4asQudiqfZJ_mMljX-6_3kGpffNdDJpQqCtq7GzLSnMA-vYoJ908OPS15U4nQjZemvaeoUEo-vVzcnJl5EQjSSvlW4YBEbfWTaPnRYm_J7PWKwfgXQQ3w5z5zwYbQp45OAmJt1WOmSt7BuMHho0d9_gkexaZwHw8-ye6T4dQMxaX1rS79JraxbmV1g-JT6-eU_nQU0QdmdyuzAstrF6K7ALC-QWWIEQImo-v0mqpiwFm2ATnkLJAa0iazQVOi9Eytvl3xKGNUXnqWAKNEVabAyxslyzur4u0k3Zwxl7VPkxP72GvXamwBe8pIEOTt3lXPdos10SO5K52aK0FWFyTTXrT3sHqaJX9nEGxHc5KMIs4MmK3c_DWMtKZwsDzltKvS77Xhema3YXW9bIK8uGLEetH02fY9G-cn_laZKed0nb0mVLem-k--rnaVGc_sLfGHyE1mvmIp029PFEnkPO5r99H0AvQ8YAqOtsjr4lsx1_pGffJ5HHpnoO8w677HJiQYE-LXb7P8O3QhRk2wYemsAvqx6pM_puIDLLdEoCVUldd7NJgiKLEF3Zse5htQifkPgHwCcEFg==?id=figure-oidc-authz-pkce)Figure 1. OpenID Connect Authorization Code Grant with PKCE Flow

> **Collapse: Authorization code grant with PKCE flow explained**
>
> 1. The end user wants to use the services provided by the relying party. The relying party, usually a web-based service, requires an account to provide those services.
>
>    The end user issues a request to the relying party to access their information which is stored in an OpenID provider.
>
> 2. To access the end user's information in the provider, the relying party requires authorization from the end user. When using the PKCE standard, the relying party must generate a unique code and a way to verify it, and append the code to the request for the authorization code.
>
> 3. The relying party redirects the end user's user-agent with `code_challenge` and `code_challenge_method`…​
>
> 4. ... to the OpenID provider.
>
> 5. The OpenID provider authenticates the end user, confirms resource access, and gathers consent if not previously saved.
>
> 6. If the end user's credentials are valid and they consent to provide their data to the relying party, the OpenID provider stores the code challenge and its method.
>
> 7. The OpenID provider redirects the end user's user agent to the redirection URI (usually the relying party).
>
> 8. During the redirection process, the OpenID provider appends an authorization code.
>
> 9. The relying party receives the authorization code and authenticates to the OpenID provider to exchange the code for an access token and an ID token (and a refresh token, if applicable), appending the verification code to the request.
>
> 10. The OpenID provider verifies the code challenge stored in memory using the validation code. It also verifies the authorization code.
>
> 11. If both codes are valid, the OpenID provider returns an access and an ID token (and a refresh token, if applicable) to the relying party.
>
> 12. The relying party validates the ID token and its claims.
>
>     Now, the relying party can use the ID token subject ID claim as the end user's identity.
>
> 13. The relying party may require more claims than those included in the ID token. In this case, it makes a request to AM's `oauth2/userinfo` endpoint with the access token.
>
> 14. If the access token is valid, the `oauth2/userinfo` endpoint returns additional claims, if any.
>
>     Now, the relying party can use the subject ID and the additional retrieved claims as the end user's identity.

## Generate a code Verifier and a code challenge

The relying party (the client) must be able to generate a code verifier and a code challenge. For details, see the PKCE standard ([RFC 7636](https://www.rfc-editor.org/info/rfc7636)). The information contained in this procedure is for example purposes only:

1. The client generates the code challenge and the code verifier. Creating the challenge using a SHA-256 algorithm is mandatory if the client supports it, as per the RFC 7636 standard.

   The following is an example of a code verifier and code challenge written in JavaScript:

   ```javascript
   function base64URLEncode(words) {
      return CryptoJS.enc.Base64.stringify(words)
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
   }
   var verifier = base64URLEncode(CryptoJS.lib.WordArray.random(50));
   var challenge = base64URLEncode(CryptoJS.SHA256(verifier));
   ```

   This example generates values such as `ZpJiIM_G0SE9WlxzS69Cq0mQh8uyFaeEbILlW8tHs62SmEE6n7Nke0XJGx_F4OduTI4` for the code verifier and `j3wKnK2Fa_mc2tgdqa6GtUfCYjdWSA5S23JKTTtPF8Y` for the code challenge. These values will be used in subsequent procedures.

   The relying party is now ready to request an authorization code.

2. The relying party performs the steps in one of the following procedures to request an authorization code:

   * [Get an authorization code using a browser](#proc-auth-code-browser-pkce-oidc)

   * [Get an authorization code without using a browser](#proc-auth-code-no-browser-pkce-oidc)

## Get an authorization code using a browser

This procedure assumes the following configuration:

* AM is configured as an OAuth 2.0/OpenID provider. Ensure that:

  * The `code` plugin is configured in the Response Type Plugins field.

  * The `Authorization Code` grant type is configured in the Grant Types field.

  The Code Verifier Parameter Required setting (Realms > *Realm Name* > Services > OAuth2 Provider > Advanced) specifies whether AM requires clients to include a code verifier in their calls. However, if a client makes a call to AM with the `code_challenge` parameter, AM will honor the code exchange regardless of the configuration of the Code Verifier Parameter Required drop-down menu.

* A *public* client called `myClient` is registered in AM with the following configuration:

  * **Scopes**: `openid profile`

  * **Response Types**: `code`

  * **Grant Types**: `Authorization Code`

  * **Token Endpoint Authentication Method**: `none`

    If you were using a confidential OpenID Connect client, you must specify a method to authenticate. For more information, see [OpenID Connect client authentication](oidc-client-auth.html).

For more information, see [Dynamic client registration](oauth2-dynamic-client-registration.html).

Perform the steps in this procedure to obtain an authorization code using a browser:

1. The relying party redirects the end user's user-agent to the AM's authorization endpoint specifying, at least, the following query parameters:

   * **client_id**=*your-client-id*

   * **response_type**=`code`

   * **redirect_uri**=*your-redirect-uri*

   * **code_challenge**=*your-code-challenge*

   * **code_challenge_method**=`S256`

   * **scope**=`openid profile`

   For information about the parameters supported by the `/oauth2/authorize` endpoint, see [/oauth2/authorize](../oauth2-guide/oauth2-authorize-endpoint.html).

   If the OAuth 2.0/OpenID provider is configured for a subrealm rather than the Top Level Realm, you must specify it in the endpoint. For example, if the OAuth 2.0/OpenID provider is configured for the `/alpha` realm, then use `/oauth2/realms/root/realms/alpha/authorize`.

   For example:

   ```none
   https://openam.example.com:8443/openam/oauth2/realms/root/realms/alphaauthorize \
   ?client_id=myClient \
   &response_type=code \
   &scope=openid%20profile \
   &redirect_uri=https://www.example.com:443/callback \
   &code_challenge=j3wKnK2Fa_mc2tgdqa6GtUfCYjdWSA5S23JKTTtPF8Y \
   &code_challenge_method=S256 \
   &nonce=123abc \
   &state=abc123
   ```

   Note that the URL is split and spaces have been added for readability purposes. The `state` and `nonce` parameters have been included to protect against CSRF and replay attacks.

2. The end user authenticates to AM, for example, using the credentials of the `demo` user.

   In this case, they log in using the default chain or tree configured for the realm.

   After logging in, AM presents its consent screen:

   ![The OpenID Conenct AM user interface consent screen requesting access to the profile scope.](_images/oidc-consent.png)Figure 2. OpenID Connect Consent Screen

3. The end user clicks Allow to grant consent for the `profile` scope.

   AM redirects the end user to the URL specified in the `redirect_uri` parameter.

4. Inspect the URL in the browser.

   It contains a `code` parameter with the authorization code AM has issued. For example:

   ```none
   https://www.example.com:443/callback?code=g5B3qZ8rWzKIU2xodV_kkSIk0F4&iss=https://openam.example.com:8443/openam/oauth2&state=abc123&client_id=myClient
   ```

5. The client performs the steps in [Exchange an authorization code for an ID/access token](#proc-auth-code-token-pkce-oidc) to exchange the authorization code for an ID/access token.

## Get an authorization code without using a browser

This procedure assumes the following configuration:

* AM is configured as an OAuth 2.0/OpenID provider. Ensure that:

  * The `code` plugin is configured in the Response Type Plugins field.

  * The `Authorization Code` grant type is configured in the Grant Types field.

  The Code Verifier Parameter Required drop-down menu (Realms > *Realm Name* > Services > OAuth2 Provider > Advanced) specifies whether AM require clients to include a code verifier in their calls. However, if a client makes a call to AM with the `code_challenge` parameter, AM will honor the code exchange regardless of the configuration of the Code Verifier Parameter Required drop-down menu.

* A *public* client called `myClient` is registered in AM with the following configuration:

  * **Scopes**: `openid profile`

  * **Response Types**: `code`

  * **Grant Types**: `Authorization Code`

  * **Redirection URIs**: `https://www.example.com:443/callback`

  * **Token Endpoint Authentication Method**: `none`

    Confidential OpenID Connect clients can use several methods to authenticate. For more information, see [OpenID Connect client authentication](oidc-client-auth.html).

For more information, see [Dynamic client registration](oauth2-dynamic-client-registration.html).

Perform the steps in this procedure to obtain an authorization code:

1. The end user logs in to AM, for example, using the credentials of the `demo` user.

   For example:

   ```bash
   $ curl \
   --request POST \
   --header "Content-Type: application/json" \
   --header "X-OpenAM-Username: demo" \
   --header "X-OpenAM-Password: Ch4ng31t" \
   --header "Accept-API-Version: resource=2.0, protocol=1.0" \
   'https://openam.example.com:8443/openam/json/realms/root/realms/alpha/authenticate'
   {
       "tokenId":"AQIC5wM…​TU3OQ*",
       "successUrl":"/openam/console",
       "realm":"/alpha"
   }
   ```

2. The client makes an HTTP POST request to AM's authorization endpoint, specifying in a cookie the SSO token of the `demo` and, at least, the following parameters:

   * **client_id**=*your-client-id*

   * **response_type**=`code`

   * **redirect_uri**=*your-redirect-uri*

   * **decision**=`allow`

   * **csrf**=*demo-user-SSO-token*

   * **code_challenge**=*your-code-challenge*

   * **code_challenge_method**=`S256`

   * **scope**=`openid profile`

     You can configure the `openid` scope as a default scope in the client profile or the OAuth 2.0/OpenID provider to avoid including the scope parameter in your calls, if required.

     However, since the `openid` scope is required in OpenID Connect flows, the example specifies it.

   For information about the parameters supported by the `/oauth2/authorize` endpoint, see [/oauth2/authorize](../oauth2-guide/oauth2-authorize-endpoint.html).

   If the OAuth 2.0/OpenID provider is configured for a subrealm rather than the Top Level Realm, you must specify it in the endpoint. For example, if the OAuth 2.0/OpenID provider is configured for the `/alpha` realm, then use `/oauth2/realms/root/realms/alpha/authorize`.

   For example:

   ```bash
   $ curl --dump-header - \
   --request POST \
   --Cookie "iPlanetDirectoryPro=AQIC5wM…​TU3OQ*" \
   --data "redirect_uri=https://www.example.com:443/callback" \
   --data "scope=openid profile" \
   --data "response_type=code" \
   --data "client_id=myClient" \
   --data "csrf=AQIC5wM…​TU3OQ*" \
   --data "state=abc123" \
   --data "nonce=123abc" \
   --data "decision=allow" \
   --data "code_challenge=j3wKnK2Fa_mc2tgdqa6GtUfCYjdWSA5S23JKTTtPF8Y" \
   --data "code_challenge_method=S256" \
   "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/authorize"
   ```

   Note that the `state` and `nonce` parameters have been included to protect against CSRF and replay attacks.

   If AM is able to authenticate the user and the client, it returns an HTTP 302 response with the authorization code appended to the redirection URL:

   ```
   HTTP/1.1 302 Found
   Server: Apache-Coyote/1.1
   X-Frame-Options: SAMEORIGIN
   Pragma: no-cache
   Cache-Control: no-store
   Date: Mon, 30 Jul 2018 11:42:37 GMT
   Accept-Ranges: bytes
   Location: https://www.example.com:443/callback?code=g5B3qZ8rWzKIU2xodV_kkSIk0F4&iss=https%3A%2F%2Fopenam.example.com%3A8443%2Fopenam%2Foauth2&state=abc123&client_id=myClient
   Vary: Accept-Charset, Accept-Encoding, Accept-Language, Accept
   Content-Length: 0
   ```

3. Perform the steps in [Exchange an authorization code for an ID/access token](#proc-auth-code-token-pkce-oidc) to exchange the authorization code for an ID/access token.

## Exchange an authorization code for an ID/access token

1. Ensure the client has obtained an authorization code by performing the steps in either [Get an authorization code using a browser](#proc-auth-code-browser-pkce-oidc) or [Get an authorization code without using a browser](#proc-auth-code-no-browser-pkce-oidc).

2. The client creates a POST request to the token endpoint in the authorization server specifying, at least, the following parameters:

   * **grant_type**=`authorization_code`

   * **code**=*your-authorization-code*

   * **client_id**=*your-client-id*

   * **redirect_uri**=*your-redirect-uri*

   * **code_verifier**=*your-code-verifier*

   For information about the parameters supported by the `/oauth2/access_token` endpoint, see [/oauth2/access_token](../oauth2-guide/oauth2-access_token-endpoint.html).

   For example:

   ```bash
   $ curl \
   --request POST \
   --data "grant_type=authorization_code" \
   --data "code=g5B3qZ8rWzKIU2xodV_kkSIk0F4" \
   --data "client_id=myClient" \
   --data "redirect_uri=https://www.example.com:443/callback" \
   --data "code_verifier=ZpJiIM_G0SE9WlxzS69Cq0mQh8uyFaeEbILlW8tHs62SmEE6n7Nke0XJGx_F4OduTI4" \
   "https://openam.example.com:8443/openam/oauth2/realms/root/realms/alpha/access_token"
   ```

   The `client_id` and the `redirection_uri` parameters specified in this call must match those used as part of the authorization code request, or AM will not validate the code.

   AM returns an ID and an access token. For example:

   ```json
   {
      "access_token":"cnM3nSpF5ckCFZOaDem2vANUdqQ",
      "scope":"openid profile",
      "id_token":"eyJ0eXAiOiJKV1QiLCJra…​7r8soMCk8A7QdQpg",
      "token_type":"Bearer",
      "expires_in":3599
   }
   ```

   If the client does not require the access token, [revoke it](../oauth2-guide/varlist-oauth2-token-revoke-endpoint.html).

   |   |                                                                                                                                                                         |
   | - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
   |   | AM can also issue refresh tokens at the same time the access tokens are issued. For more information, see [Refresh tokens](../oauth2-guide/oauth2-refresh-tokens.html). |

3. The relying party can request additional claims about the end user from AM.

   For more information, see [/oauth2/userinfo](rest-api-oidc-userinfo-endpoint.html).
