Proof Key for Code Exchange (PKCE)

PKCE (pronounced "pixy") is a security extension to OAuth 2.0 for public clients on mobile devices, designed to prevent interception of the authorisation code by a malicious application that has sneaked into the same device. The introduction to the RFC 7636 explains mechanics of such an attack.

When to use PKCE?

  • You have a native OAuth 2.0 client, such as an app on a mobile device, or a desktop app;

  • And, the OAuth 2.0 client is public, i.e. it doesn’t have credentials for authenticating securely at the token endpoint of the OAuth 2.0 server.

How to make an authorisation request with PKCE (using the recommended SHA-256 method)?

// The registered callback URI for the client app
URI redirectURI = URI.create("app://oauth-callback");

// Generate new random string to link the callback to the authZ request
State state = new State();

// Generate a new random 256 bit code verifier for PKCE
CodeVerifier codeVerifier = new CodeVerifier();

// Build the actual OAuth 2.0 authorisation request
AuthorizationRequest request = new AuthorizationRequest.Builder(
        new ResponseType("code"), 
        new ClientID("123"))
    .endpointURI(new URI("https://c2id.com/login"))
    .redirectionURI(URI.create("app://oauth-callback"))
    .scope(new Scope("read", "write"))
    .state(state)
    .codeChallenge(codeVerifier, CodeChallengeMethod.S256)
    .build();

// Send user with authz request to OAuth 2.0 server...
request.toURI();

Upon success the OAuth 2.0 server will pass back an authorisation code via the browser, which the client can then exchange for the desired token(s):

// The obtained authorisation code
AuthorizationCode code = authzResponse.getAuthorizationCode();

// Make the token request, with PKCE
TokenRequest tokenRequest = new TokenRequest(
    URI.create("https://c2id.com/token"),
    new ClientID("123"),
    new AuthorizationCodeGrant(code, redirectURI, pkceVerifier));

tokenRequest.toHTTPRequest().send();

Note: The PKCE API was updated in v5.20 of the OAuth 2.0 / OpenID Connect SDK to prevent some common mistakes that developers have made in its usage. The example above is based on this newer improved API.

PKCE with OpenID Connect

OpenID Connect, which is based on the OAuth 2.0 framework, also supports PKCE:

// The registered callback URI for the client app
URI redirectURI = URI.create("app://oauth-callback");

// Generate new random string to link the callback to the authZ request
State state = new State();

// Generate a new random 256 bit code verifier for PKCE
CodeVerifier codeVerifier = new CodeVerifier();

// Build the actual OpenID authentication request
AuthenticationRequest request = new AuthenticationRequest.Builder(
        new ResponseType("code"),
        new Scope("openid"),
        new ClientID("123"),
        redirectURI)
    .endpointURI(new URI("https://c2id.com/login"))
    .state(state)
    .codeChallenge(pkceVerifier, CodeChallengeMethod.S256)
    .build();

The Connect2id server added support for PKCE in version 3.9.