Proof Key for Code Exchange (PKCE)
OAuth 2.0 public clients utilizing 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").
Uses Proof Key for Code Exchange (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 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 '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 since it has no means of keeping them safe from misuse.
Public clients are defenseless against authorization code inspection attacks specially 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 access token subsequently gaining access to data they shouldn't be allowed access to.
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 of 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 OP (openID provider), the user will be redirected back to the SP (service provider) application, with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at OP, he/she 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_NONE \
--data client_secret=47f3aecc-6822-4a5a-8f15-44655c065b02 \
--data redirect_uri=https://www.client-app.com/ \
--data code=b5b66243-37a9-4337-8019-238fc7ed0b8d
Please note that you need to pass client_secret
when using a client registered with code_challenge_method=none
Code challenge method S256
In this flow, the client has to create the a random string and hashes it and encodes 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 which specifies the method used for creating the code challenge.
Example of 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'
Please note that the code_challenge_method is optional, and if it's not explicitly set to S256, it will default to plain, and this can cause issues when exchanging the code for getting an access token because the request will fail as the code verifier will not match with the code challenge.
If the user is logged in at OP (openID provider), the user will be redirected back to the SP (service provider) application, with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at OP, he/she 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_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
. Once OP will receive this request, it will verify the code verifier, by applying the formula mentioned above, and verify if the hashed match. If the hashes are matching, 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 of 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 OP (openID provider), the user will be redirected back to the SP (service provider) application, with the code, otherwise it will be redirected back to the OP login page. Assuming that the user already has a session at OP, he/she 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