? Back to Blog

PKCE and public clients

Brendan G · 2026-04-22

What is PKCE?

PKCE, also known as Proof Key for Code Exchange, is an extension to the OAuth 2.0 authorization framework designed to protect public clients from authorization code interception attacks, which can compromise the security of the authorization flow. In traditional OAuth 2.0, public clients use a client secret to authenticate with the authorization server, which can be vulnerable to interception attacks.

PKCE introduces a new challenge-response mechanism that allows public clients to prove their identity without exposing their client secret. This is achieved through a client-generated code verifier, which is exchanged for a code challenge with the authorization server. The code verifier and code challenge are generated and processed using a secure hash function, such as SHA-256.

How does PKCE work?

The PKCE workflow is a crucial aspect of understanding how this extension works. Here's a step-by-step explanation:

  • The public client generates a code verifier, which is a cryptographically secure random string. The code verifier is typically 43 characters long and is generated using a secure random string generator.
  • The public client creates a code challenge by hashing the code verifier using a hash function (e.g., SHA-256). The code challenge is typically a string of 43 characters and is used to prove the client's identity.
  • The public client includes the code challenge in the authorization request to the authorization server. The code challenge is typically passed as a query parameter in the authorization request.
  • The authorization server verifies the code challenge by hashing it and comparing it with the code verifier provided by the public client. If the code challenge is valid, the authorization server issues an authorization code, which can be exchanged for an access token.
  • The public client can then use the authorization code to obtain an access token from the authorization server. The access token can be used to access the protected resources on behalf of the user.

Why is PKCE essential for public clients?

PKCE provides several benefits for public clients:

  • Security**: PKCE protects public clients from authorization code interception attacks, which can compromise the security of the authorization flow.
  • Client secret protection**: PKCE allows public clients to prove their identity without exposing their client secret, which reduces the risk of client secret exposure.
  • Improved usability**: PKCE simplifies the authorization flow by eliminating the need for a client secret in the authorization request.
  • Better support for mobile and web applications**: PKCE is particularly useful for mobile and web applications that need to authenticate users and access protected resources.

Implementing PKCE in public clients

To implement PKCE in public clients, follow these steps:

  • Generate a code verifier**: Use a cryptographically secure random string generator to create a code verifier. The code verifier should be 43 characters long and generated using a secure random string generator.
  • Create a code challenge**: Hash the code verifier using a hash function (e.g., SHA-256) to create a code challenge. The code challenge should be 43 characters long and used to prove the client's identity.
  • Include the code challenge in the authorization request**: Add the code challenge to the authorization request to the authorization server. The code challenge should be passed as a query parameter in the authorization request.
  • Verify the code challenge**: Verify the code challenge by hashing it and comparing it with the code verifier provided by the public client. If the code challenge is valid, the authorization server issues an authorization code, which can be exchanged for an access token.

Best practices for PKCE implementation

To ensure secure PKCE implementation, follow these best practices:

  • Use a cryptographically secure random string generator**: Use a secure random string generator to create the code verifier. This ensures that the code verifier is generated randomly and securely.
  • Hash the code verifier using a secure hash function**: Use a secure hash function (e.g., SHA-256) to create the code challenge. This ensures that the code challenge is generated securely and cannot be tampered with.
  • Store the code verifier securely**: Store the code verifier securely to prevent unauthorized access. This can be done by storing the code verifier in a secure database or encrypted storage.
  • Regularly update the code verifier**: Regularly update the code verifier to prevent reuse of the same code verifier. This can be done by generating a new code verifier at regular intervals (e.g., every hour).
  • Use a secure transport protocol**: Use a secure transport protocol (e.g., HTTPS) to protect the authorization request and response. This ensures that the authorization request and response are transmitted securely and cannot be tampered with.
  • Implement PKCE in a secure environment**: Implement PKCE in a secure environment that enforces secure coding practices and follows best practices for secure coding.

PKCE and public clients in the wild

PKCE is widely used in public clients, including web and mobile applications. Some popular frameworks and libraries that support PKCE include:

  • OAuth 2.0 libraries for Java, Python, and .NET
  • OpenID Connect libraries for Java, Python, and .NET
  • Auth0, Okta, and other identity and access management platforms
  • Google's OAuth 2.0 client library for Java and Python
  • Microsoft's OAuth 2.0 client library for .NET

Example PKCE implementation in Java

Here is an example PKCE implementation in Java using the OAuth 2.0 client library:

```java import java.security.SecureRandom; import java.util.Base64; public class PKCEExample { public static void main(String[] args) { // Generate a code verifier SecureRandom random = new SecureRandom(); byte[] codeVerifierBytes = new byte[43]; random.nextBytes(codeVerifierBytes); String codeVerifier = new String(Base64.getUrlEncoder().withoutPadding().encode(codeVerifierBytes)); // Create a code challenge String codeChallenge = getSHA256(codeVerifier); // Include the code challenge in the authorization request String authorizationRequest = "https://example.com/oauth2/authorize?" + "client_id=client_id&" + "response_type=code&" + "redirect_uri=https://example.com/callback&" + "code_challenge=" + codeChallenge + "&" + "code_challenge_method=S256"; // Verify the code challenge String authorizationCode = getAuthorizationCode(authorizationRequest); String codeVerifierFromAuthorizationServer = getVerifierFromAuthorizationServer(authorizationCode); if (getSHA256(codeVerifier).equals(codeChallenge)) { // The code challenge is valid } } private static String getSHA256(String input) { // Implement SHA-256 hashing } private static String getAuthorizationCode(String authorizationRequest) { // Implement authorization code retrieval } private static String getVerifierFromAuthorizationServer(String authorizationCode) { // Implement verifier retrieval from authorization server } } ```

Conclusion

PKCE is an essential extension to OAuth 2.0 that protects public clients from authorization code interception attacks. By implementing PKCE, public clients can ensure secure authentication and authorization flows. Follow the best practices outlined in this article to ensure secure PKCE implementation and protect your users' sensitive data.

Remember to use a cryptographically secure random string generator to create the code verifier, hash the code verifier using a secure hash function, store the code verifier securely, regularly update the code verifier, and use a secure transport protocol to protect the authorization request and response.

By following these best practices, you can ensure a secure and reliable PKCE implementation in your public clients.

Join the affiliate program and earn 50%. No approvals, no waitlists.