Authorization Code with Proof Key for Code Exchange (PKCE) is an OAuth 2.1 flow designed for both public
clients — such as single-page and mobile applications — and confidential clients, including server-side web applications.
By executing a sequence of web requests, the user is authenticated, resulting in the issuance of an access_token
.
The PKCE standard is described in rfc7636.
The PKCE flow requires user interaction, meaning the user is redirected to the authorization server to provide their authentication credentials (e.g., username/password, social login, etc.).
After successful authentication, the user is redirected back to the site that initiated the authorization request. The redirect includes an authorization code.
Up to this point, the process is the standard Authorization Code flow. However, what makes Authorization Code + PKCE different is the fact that at the start of the authorization request, the initiating application generates a challenge.
Upon receiving the authorization code, the application then sends it to the authorization server to exchange it for an access token. However, the server will only issue the token if the original challenge (created at the beginning of the flow) is presented correctly.
This mechanism makes it significantly harder for attackers to intercept and misuse the authorization code.
The process with PKCE is illustrated in the following sequence diagram:
The core concept of PKCE (Proof Key for Code Exchange) is the code challenge.
This challenge is generated at the beginning of the authorization flow and is later sent to the authorization server
again when exchanging the authorization code for a token via the /token
endpoint.
There are two supported methods for communicating the challenge to the authorization server:
plain
S256
(recommended)
When using the plain
method, the same value is used for both the challenge and the verifier.
The challenge is included directly in the authorization request URL:
GET /authorize? response_type=code& client_id=your-client-id& redirect_uri=https://yourapp.com/callback& scope=openid profile email& state=xyz123& code_challenge=abc123plainverifier& code_challenge_method=plain
The corresponding token request includes the same value as the code_verifier
:
POST /token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AUTH_CODE_FROM_CALLBACK& redirect_uri=https://yourapp.com/callback& client_id=your-client-id& code_verifier=abc123plainverifier
The more secure and widely recommended method is S256
,
which involves hashing the code verifier before sending it in the authorization request.
code_verifier = T0pSecret!Code+Verifier123 code_challenge = BASE64URL-ENCODE(SHA256(code_verifier) // results in this string: VnYfSg3ZkAZTr9nMk6ojq2mr_wIBJxn89Dbk5m97B9o
The resulting authorization request includes the hashed value:
GET /authorize? response_type=code& client_id=your-client-id& redirect_uri=https://yourapp.com/callback& scope=openid profile email& state=xyz123& code_challenge=VnYfSg3ZkAZTr9nMk6ojq2mr_wIBJxn89Dbk5m97B9o& code_challenge_method=S256
When exchanging the code for a token, the original, unhashed code_verifier
must be provided:
POST /token Content-Type: application/x-www-form-urlencoded grant_type=authorization_code& code=AUTH_CODE_FROM_CALLBACK& redirect_uri=https://yourapp.com/callback& client_id=your-client-id& code_verifier=T0pSecret!Code+Verifier123
Compared to the standard OAuth 2.0 Authorization Code Flow, PKCE introduces additional parameters to the /authorize
request.
This includes decisions like whether to use a plain
or S256
code challenge method.
Typically, the authorize request is generated using client libraries, which encapsulate all necessary logic to build a secure and standards-compliant URI. These libraries handle details like challenge generation, state parameters, and proper URL formatting. Common libraries include:
Not every OAuth server supports PKCE.
Whether a server supports PKCE is indicated in the discovery document.
If supported, the document should include the code_challenge_methods_supported
property,
which lists the available challenge methods.
"code_challenge_methods_supported": [ "S256", "plain" ],
Like the Implicit Flow, PKCE is intended to be used with apps that run in the browser. The key difference that makes PKCE safer than the Implicit Flow is that it introduces additional mechanisms to ensure that the token can only be received by the intended client.
Where the Implicit Flow sends the access_token
to any approved redirect_uri
,
PKCE protects against authorization code interception attacks by ensuring that the authorization code
cannot be exchanged for a token without the original code verifier.
PKCE adds an extra layer of security to the OAuth 2.1 Authorization Code flow by ensuring that intercepted authorization codes cannot be reused by malicious parties. This is particularly important for public clients like single-page applications and mobile apps, where storing client secrets securely isn't feasible.
By generating a code_verifier
and deriving a code_challenge
,
applications ensure that only the original requester can complete the token exchange.