Leveraging Proof Key for Code Exchange (PKCE) in the Authorization Code Grant Flow

To mitigate against the threat of authorization code interception attacks, the ActivID AS server supports Proof Key for Code Exchange (PKCE). For public clients, it is required to use PKCE.

For further information about PKCE, see the OAuth 2.0 PKCE specification.

PKCE Usage in Authorization Code Flow

  1. The client dynamically creates a unique ‘Code Verifier’ (a cryptographic random key) for the authorization request using the following format:

    code-verifier = 43*128unreserved

    unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"

    ALPHA = %x41-5A / %x61-7A

    DIGIT = %x30-39

    The code verifier is then generated by encoding the 43-octet sequence in Base64URL.

  2. The client creates the ‘Code Challenge’ by transforming the value of the unique code verifier for the authorization request using one of the following code_challenge_method options:

    • plain:

      code_challenge = code_verifier

    • S256:

      code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

  1. The client sends the Code Challenge with the Authorization Request to the authorization server to obtain the authorization code.

    For example:

    Copy

    code_challenge (plain)

    NDdERVFwajhIQlNhLV9USW1XLTVKQ2V1UWVSa201Tk1wSldaRzNoU3VGVQ
    Copy

    URL invoked with the code_challenge_method as plain

    https://server.example.com:8445/idp/domain/authn/login?client_id=spl-api&response_type=code&scope=openid+profile&redirect_uri=https://localhost:8080&code_challenge_method=plain&code_challenge=NDdERVFwajhIQlNhLV9USW1XLTVKQ2V1UWVSa201Tk1wSldaRzNoU3VGVQ
    Copy

    code_challenge (S256)

    45ee543e8b243eef8cc086a695c14b73ba0edc2d1bedaeb6549b5dde6f6a2d49
    Copy

    URL invoked with the code_challenge_method as S256

    https://server.example.com:8445/idp/domain/authn/login?response_type=code&client_id=OpenID_admin&redirect_uri=http://localhost&scope=openid&code_challenge_method=S256&code_challenge=45ee543e8b243eef8cc086a695c14b73ba0edc2d1bedaeb6549b5dde6f6a2d49
    Note: The code_challenge_method is optional. If it is not present, the default value is plain.
  1. The server redirects the response to the predefined redirect_uri with a valid code.

    For example:

    Copy
    http://server.example.com:8445/redirect/?code=35256753&context=PARAMETER_AUTHENTICATION_TYPE_CODE%3AAT_EMPPWD%3Afalse+LEVEL_OF_ASSURANCE%3A2%3Afalse+
  2. The client (that requested the authorization code) sends the obtained Authorization Code and the Code Verifier to the token endpoint as the Access Token Request.

    The request also includes the client authentication (such as client_secret_basic. or the bearer access token) for a confidential client. For the public client, the client authentication is not sent.

    • For confidential clients:
      • PKCE is not required, but it is supported
      • Client authentication should be sent (client basic, bearer token etc.)
    • For public clients:
      • PKCE is required
      • No client credential is sent
      • code_verifier is required
    Copy

    Sample request (with a confidential client)

    POSThttps://[base-server-url]/{tenant}/authn/token HTTP/1.1
    Accept-Encoding:gzip,deflate
    Content-Type: application/x-www-form-urlencoded
    Authorization: Bearer Z+KhiAAAAWNzjH1N2CIYsVDj3QN4Q+QBlDfS1c/h
     
    grant_type=authorization_code&code=35256753&redirect_uri=http%3A%2F%2Flocalhost&client_id=OpenID_admin&code_verifier=NDdERVFwajhIQlNhLV9USW1XLTVKQ2V1UWVSa201Tk1wSldaRzNoU3VGVQ
    Copy

    Sample request (with a public client)

    POSThttps://[base-server-url]/{tenant}/authn/token HTTP/1.1
    Content-Type:application/x-www-form-urlencoded
     
    grant_type=authorization_code&code=17312836&redirect_uri=http%3A%2F%2Flocalhost&client_id=OpenID_admin&code_verifier=NDdERVFwajhIQlNhLV9USW1XLTVKQ2V1UWVSa201Tk1wSldaRzNoU3VGVQ

    If the code_verifier is not present, an error will be returned.

  1. The code verification is performed during Access Token retrieval. The server compares the Code Verifier (based on the code_challenge_method) with the previously received request code (proof of possession of the "code verifier" by the client) before returning the tokens:

    • If the method was S256, the Code Verifier is hashed and encoded, and then compared to the Code Challenge:

      BASE64URL-ENCODE(SHA256(ASCII(code_verifier))) == code_challenge

    • If the method was plain, the Verifier and Challenge codes are compared directly:

      code_verifier == code_challenge

    • If the verification is successful, the server returns the access token. Otherwise, it returns errors.

Sample Error Responses

Copy

Client does not support public clients

HTTP/1.1 400 Bad Request
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
 
{
    "error_description":"Unauthorized client",
    "error":"unauthorized_client"
}
Copy

The request does not contain a code_verifier

HTTP/1.1 403 Forbidden
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
 
{
    "error_description":"Access denied by resource owner or authorization server:Code verifier missing",
    "error":"access_denied"
}
Copy

The code is invalid, or the client_id, direct_uri do not match

HTTP/1.1 400 Bad Request
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
 
{
    "error_description":"Invalid grant",
    "error":"invalid_grant"
}