Proof Key for Code Exchange
OAuth 2.0 public clients using the authorization code grant are susceptible to the authorization code interception attack. This specification describes the attack, as well as a technique to mitigate the threat through the use of Proof Key for Code Exchange (PKCE, pronounced pixy).
Use PKCE instead of a client secret. A one-time key is generated by the client and sent with each request. Instead of proving the identity of a client, this ensures that only the client that requested the token can redeem it.
PKCE can be configured on the OAuth2 client, and the possible values can be seen in the table below:
Value | Description |
---|---|
none | This means that the PKCE flow is disabled. |
any | PKCE is enforced, but the method is selected based on the parameter in the request. This mode can be used to be compliant with RFC7636, indicating "plain" to be the default if the method is not provided in the request. |
S256 | PKCE is enforced with the 'S256' method. |
Why do we need PKCE?
There are two types of clients in OAuth 2.0, confidential clients and public clients. "Confidential clients are clients who have the capability of maintaining the confidentiality of their credentials. Public clients are clients who don't have the capability of maintaining the confidentiality of their credentials." In other words, a public client can't be trusted with credentials, because it has no means of keeping them safe from misuse.
Public clients are defenseless against authorization code inspection attacks, especially when the communication path is not protected by Transport Layer Security(TLS). Attackers can easily get the authorization code from the authorization endpoint and they can use it to obtain an access token, subsequently gaining access to data they shouldn't be allowed to access.
Example of requests with all PKCE options
Code challenge method none
As mentioned above, none
means that PKCE support is disabled, and therefore the normal OAuth 2.0/OIDC flow is performed.
Example requests
Authorize request
curl --request GET \
--url 'https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/authorize?response_type=code&client_id=CLIENT_WITH_NONE&redirect_uri=https://www.client-app.com/&scope=openid&state=smth'
If the user is logged in at the OpenID provider (OP), the user will be redirected back to the service provider (SP) application with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at the OP, they will be redirected back to the SP application, and the next request will be the one below:
Token request
curl --request POST \
--url https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/token \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data client_id=CLIENT_WITH_NONE \
--data client_secret=47f3aecc-6822-4a5a-8f15-44655c065b02 \
--data redirect_uri=https://www.client-app.com/ \
--data code=b5b66243-37a9-4337-8019-238fc7ed0b8d
Note
You need to pass the client_secret
when using a client registered with code_challenge_method=none
.
Code challenge method S256
In this flow, the client has to create a random string, hash it, and encode it using the following formula: code_challenge=BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
. This string is sent in the authorization request in a new query parameter called code_challenge
, along with the code_challenge
method that specifies the method used for creating the code challenge.
Example requests
Authorization request
curl --request GET \
--url 'https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/authorize?response_type=code&client_id=CLIENT_WITH_S256&redirect_uri=https://www.client-app.com/&scope=openid&state=smth&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256'```
!!! note
The `code_challenge_method` is optional, and if it's not explicitly set to `S256`, it will default to `plain`. This can cause issues when exchanging the code for getting an access token, because the request will fail when the code verifier does not match the code challenge.
If the user is logged in at the OpenID provider (OP), the user will be redirected back to the service provider (SP) application with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at the OP, they will be redirected back to the SP application, and the next request will be the below one:
##### Token request
```curl
curl --request POST \
--url https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/token \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data client_id=CLIENT_WITH_S256 \
--data redirect_uri=https://www.client-app.com/ \
--data code=c1852ce3-2c44-4b71-ae86-7af0ba6ad5b4 \
--data code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
The request contains the code_verifier
query parameter, which substitutes the client_secret
. When the OP will receive this request, it will verify the code verifier, by applying the formula mentioned above, and verify if the hashes match. If the hashes match, assuming that all the other attributes are correct, it will issue an access token.
Code challenge method plain
Using the code challenge plain
implies not hashing the code challenge, and therefore, in this situation, the following formula will apply: code_challenge
= code_verifier
.
Example requests
Authorization request:
curl --request GET \
--url 'https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/authorize?response_type=code&client_id=CLIENT_WITH_ANY&redirect_uri=https://www.client-app.com/&scope=openid&state=smth&code_challenge=e9MelHWQ2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-XV'
If the user is logged in at the OpenID provider (OP), the user will be redirected back to the service provider (SP) application with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at the OP, they will be redirected back to the SP application, and the next request will be the below one:
Token request
curl --request POST \
--url https://www.onewelcome.com/onewelcome-dev/auth/oauth2.0/v1/token \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=authorization_code \
--data client_id=CLIENT_WITH_ANY \
--data redirect_uri=https://www.client-app.com/ \
--data code=c1852ce3-2c44-4b71-ae86-7af0ba6ad5b4 \
--data code_verifier=e9MelHWQ2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-XV