Home > OpenID Connect OAuth Server dedicated > Verification Key for Code Exchange (PKCE)

Verification Key for Code Exchange (PKCE)

OAuthSD can implement the Authorization code + PKCE flow. The main difference between the Authorization code + PKCE flow and the standard flow is that the client does not provide the application secret, but a verification key.

On the other hand, since there is no longer a secret, there is no longer any identification of the application implemented by the client, which is therefore of the "public" type.
The method avoids propagating a secret, but does not provide any new information on the identity of the application since it is a public instance that is thus identified. Conformity of the application to the original model is not guaranteed.

PKCE (pronounced "pixy") is an acronym for "Proof Key for Code Exchange".
See RFC 7636: Proof of Key for Code Exchange (PKCE)

Usefulness of PKCE for mobile/native apps

When authenticating, mobile/native apps (without a backend) can use the authorization code flow, but they require additional security because they cannot Securely store the client secret that could be revealed by reverse-engineering (the client secret is tied to the application and is the same for all users and devices).
Apps without a backend can use a custom URL scheme to capture redirects (for example, MyApp://). This can be hijacked by a malicious application, residing on the same user-agent, to receive an authorization code.

To address this issue, OAuth 2.0 and OpenID Connect provide a complement to the authorization code flow using a Verification Key for Code Exchange (PKCE).

Authorization code flow + PKCE

A client application implementing the authorization code flow with PKCE must be of the "public" type, i.e. be registered on the server with a null secret.

The public client application creates a random (unique and containing no information) string value, the code_verifier. When the client application initiates the authorization code request, instead of the secret, it sends to the Authorize controller the code_verifier hash under the code_challenge parameter.

After the user is authenticated and the authorization code is returned to the client application, the client application requests the tokens in exchange for the authorization code, including the code_verifier parameter.
If the codes match, authentication is complete and an access_token is returned.

Creation of the code_challenge

In the authorization code request, the application must transmit the code_challenge and the coding method "code_challenge_method" [1].

Starting from a random string ’code_verifier’, we obtain the code_challenge by applying a SHA256 hash and base64 URL encoding. You must then pass code_challenge_method = ’S256’

PHP

  1.   // Prepare PKCE code_challenge
  2.          $code_verifier = substr( str_shuffle("abcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEFabcdefghijklmnopqrstuvwxyz0123456789ABCDEF"), 0, 50);
  3.          $code_challenge = strtr(rtrim(base64_encode(hash('sha256', $code_verifier, true)), '='), '+/', '-_');
  4.          $_SESSION['code_verifier'] = encrypt($code_verifier);
  5. ...

Download

Javascript

  1. function base64URLEncode(str) {
  2.      return str.toString('base64')
  3.          .replace(/\+/g, '-')
  4.          .replace(/\//g, '_')
  5.          .replace(/=/g, '');
  6. }
  7. var code_verifier = crypto.randomBytes(50);
  8. var code_challenge = base64URLEncode(code_verifier);
  9. ...

Download

ENFORCE_PKCE configuration constant

If the ENFORCE_PKCE configuration constant is set to true, all clients must enforce PKCE.
If false, PKCE is applied only to clients querying Authorize with the code_challenge parameter.
This constant is set to False by default.

Error ’missing_code_challenge’

If the ENFORCE_PKCE configuration constant is set to true or if the client queries Authorize with the code_challenge parameter, and the client does not provide the challenge code and encoding method in the authorization code request, the Authorize controller responds with a code 400 and the message ’missing_code_challenge’, ’This application requires you provide a PKCE code challenge’.

Call to Token controller

When the client application calls the Token controller to obtain the tokens, it passes the code_verifier parameter and the client ID in the body of the POST request (instead of passing the client ID and the secret in the header by the authentication method HTTP Basic).

Example in PHP:

  1.      $code_verifier = decrypt(@$_SESSION['code_verifier']);
  2.  
  3.      $data = array(
  4.          'client_id' => $client_id,
  5.          'grant_type' => 'authorization_code',
  6.          'code' => $code,
  7.          'code_verifier' => $code_verifier,
  8.      );
  9.  
  10.      $h = curl_init($token_endpoint);
  11.      curl_setopt($h, CURLOPT_RETURNTRANSFER, true);
  12.      curl_setopt($h, CURLOPT_TIMEOUT, 10);
  13.      //curl_setopt($h, CURLOPT_USERPWD, "{$client_id}:{$client_secret}");
  14.      curl_setopt($h, CURLOPT_POST, true);
  15.      curl_setopt($h, CURLOPT_SSL_VERIFYPEER, false);
  16.      curl_setopt($h, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
  17.      curl_setopt($h, CURLOPT_POSTFIELDS, http_build_query($data));
  18.  
  19.      $res = curl_exec($h);
  20.      ...

Download

Error ’code_verifier_missing’

If the client queried Authorize with a code_challenge (regardless of the value of the ENFORCE_PKCE configuration constant), and the client does not provide the ’code_verifier’ parameter in the token request, the Token controller responds with a 400 code and the message ’code_verifier_missing’, ’The PKCE code verifier parameter is required’.

PKCE Security Discussion

First of all, let’s remember that PKCE does not replace the secret of the application, and therefore does not provide any answer to the problem of identifying the client application.
The PKCE-enhanced authorization code flow introduces a random code "code_verifier", obscure (containing no information), created by the calling application and can be verified by the authorization server. Additionally, the calling application creates a transformed code checker value called "code_challenge" and sends this value over HTTPS to retrieve an authorization code. In this way, a malicious attacker can only intercept the authorization code and cannot exchange it for a token without the code checker.

However, after creating the verifier code in the first step of the authorization code flow, the client application must store it somewhere to provide it in the second step, when requesting the token. Even if this only lasts a very short time (of the order of a second), the code verifier is exposed to malware which clearly has everything it needs to try to obtain a token in place of the legitimate application. In principle, the legitimate application should have requested the token immediately, probably before the malware which will stumble on the ban on using the same verifier code twice. Unless the hacker is capable of changing execution priorities? Even if it seems difficult or improbable, there is perhaps an opportunity for exploit.

Footnotes

[1code_challenge_method can take the values "plain" or "S256".