What is PKCE?
PKCE, which stands for “Proof of Key Code Exchange” and is pronounced “pixy,” is an extension of the OAuth 2.0 protocol that helps prevent code interception attacks. OAuth 2.0 allows users to share their data securely between different applications, and PKCE provides an additional security layer on top of it.
Here, we will discuss how PKCE enhances the security of authorization code grant flows, explore some of the benefits, best practices, and challenges of working with PKCE, and highlight how the Postman API Platform can help simplify PKCE and OAuth workflows.
If you’re not already familiar with OAuth 2.0, we recommend that you review our “What is OAuth 2.0?” article before proceeding.
How does PKCE work?
Before we discuss how PKCE works, you must first understand the authorization code grant flow—as well as its security vulnerabilities.
When an authorization request is made, the authorization code grant type requires the authorization server to generate an authorization code, which is returned to the client application via a redirect URL. This code can then be exchanged for an access token, which can be used to access the user’s data.
An attacker can intercept the authorization code that is sent back to the client and exchange it for an access token, which can cause serious data leaks or breaches. One popular method malicious actors use to intercept authorization codes is by registering a malicious application on the user’s device. This malicious application will register and use the same custom URL scheme as the client application, allowing it to intercept redirect URLs on that URL scheme and extract the authorization code:
PKCE addresses this vulnerability by introducing a new flow and three new parameters: Code Verifier, Code Challenge, and the Code Challenge Method. Below is a breakdown of a PKCE authentication flow.
Before an authorization request is made, the client creates and stores a secret called the “code verifier.” The code verifier is a cryptographically random string that the client uses to identify itself when exchanging an authorization code for an access token. It has a minimum length of 43 characters and a maximum length of 128 characters.
The client also generates a code challenge, which is a transformation of the code verifier. The code challenge is sent with the initial authorization request, along with a code challenge method. The code challenge method is the transformation mode used to generate the code challenge. There are two code challenge methods that PKCE supports: plain and S256.
- Plain: In the plain mode, the code challenge is equal to the code verifier; nothing changes.
- S256: In S256 mode, the SHA-256 hash of the code verifier is encoded using the BASE64URL encoding. The S256 method is recommended by the specification and should be considered before the plain method.
Next, the code challenge is securely stored by the authorization server, and an authorization code is returned with the redirect URL as usual. When the client wants to exchange this authorization code for the access token, it sends a request that includes the initial code verifier. The server then hashes the code verifier using SHA-256 (if it has a code challenge method of S256) and encodes the hashed value as a BASE64URL. The corresponding value is then compared to the code challenge. If they match, an access token is issued. Otherwise, an error message is returned.
This flow ensures that a malicious third-party application cannot exchange an authorization code for an access token, since the malicious application does not have the code verifier. Intercepting the code challenge is also useless because SHA256 is a one-way hashing algorithm and cannot be decrypted.
This diagram represents the PKCE protocol flow:
What are some benefits of working with PKCE?
PKCE offers many security benefits that have made it a widely adopted standard among developers that implement OAuth 2.0. These benefits include:
- Eradication of code interception attacks: Without PKCE, an attacker that intercepts the authorization code can potentially exchange it for an access token. PKCE prevents this attack method by requiring a code verifier with every exchange, ensuring that only the original client that started the flow can obtain the token.
- Backwards compatibility: PKCE can be used with any OAuth 2.0 authorization server that supports it—while still being compatible with servers that do not. This is because PKCE is simply an extension of OAuth. Therefore, authorization servers that support PKCE can also work with regular OAuth out of the box, unless the provider makes PKCE mandatory on the authorization server.
- Standardization and wide adoption: PKCE has seen wide adoption—especially on mobile and single page applications—due to its tremendous security benefits. It is also supported by most popular OAuth libraries.
- Dynamic secrets: PKCE uses a dynamic secret that’s generated for each authorization request, which reduces the risk associated with a compromised client secret.
What are some best practices for working with PKCE?
When working with PKCE, it is important that you adhere to the following best practices to ensure that your application is properly secured:
- Generate unique code verifiers: Each authorization request should contain a uniquely generated code verifier. Generating new code verifiers helps ensure that the code challenge is not predictable and helps prevent replay attacks. Additionally, code verifiers should not be hard-coded.
- Use high-entropy code verifiers: Code verifiers should be generated using a cryptographically secure random generator, making it impossible to guess. It should have a minimum length of 43 characters and a maximum length of 128 characters.
- Use SHA-256 hashing: The plain code challenge method should only be used when the client is unable to support the S256 method. S256 is a one-way hash, which further ensures that only the client has the code verifier and can exchange an authorization code for an access token.
- Prevent downgrade attacks: A downgrade attack can occur when an authorization server supports PKCE, but does not make it mandatory. In this scenario, an attacker can intercept the client authorization request and modify it to remove the PKCE parameters (i.e., the code challenge and code challenge method). The attacker then forwards the modified request to the authorization server, which processes the request without PKCE and issues an authorization code. The attacker can then intercept the authorization code and use it maliciously. This type of attack can be prevented by enforcing PKCE on the authorization server and properly validating the code verifier to ensure that it matches the initial code challenge.
- Use with client secrets: PKCE is a security layer—not a replacement for client secrets and client authentication. It is therefore recommended to use a client secret, even with PKCE.
- Implement time limits: Code verifiers and the transformed code challenge should have a short lifespan, which prevents them from being reused repeatedly.
What are some challenges of implementing PKCE?
PKCE offers numerous benefits, but it still presents many challenges that developers should take note of. These challenges include:
- Algorithm compatibility: Different authorization servers and libraries might support different hash algorithms for transforming code verifiers into code challenges. This can make it difficult to ensure compatibility across different components.
- Complexity: PKCE introduces additional complexity to the authorization code flow implementation. Developers need to generate and manage code verifiers and challenges correctly—and handle their transformations and comparisons securely.
- Code verifier storage: PKCE requires the client to store the original code verifier until the authorization code is exchanged. Ensuring secure storage and retrieval of the verifier can be challenging, especially in public clients.
- Security misconfigurations: Implementing PKCE incorrectly or failing to properly validate code challenges can introduce security vulnerabilities. For instance, if the code challenge is not validated, attackers might forge their own challenges and bypass the security measures.
- Risk of overconfidence: While PKCE provides a strong security layer, it doesn’t eliminate the need for other security measures, such as secure communication (HTTPS), client secrets, and proper access control. Relying solely on PKCE alone can lead to security gaps.
How can Postman help you work with PKCE?
The Postman API Platform includes many features that make it easier for users to work with PKCE and OAuth 2.0. With Postman, you can:
- Leverage built-in support for OAuth: Postman includes built-in support for both OAuth 1.0 and OAuth 2.0, which can be configured at the request, collection, or folder level.
- Work seamlessly with PKCE: Postman has built-in support for PKCE when working with OAuth APIs. Postman can automatically generate your code verifiers for you, and it allows you to work with PKCE on the request and collection level.
- Refresh your OAuth 2.0 access tokens: Postman can automatically refresh OAuth 2.0 access tokens before they expire, which saves the user time by eliminating the need to repeat the entire authorization process.
- Quickly authenticate with public APIs: Postman will guide users through the authentication process for several popular APIs, including those that leverage OAuth 2.0. This feature streamlines the authentication process and significantly reduces consumers’ time to first call.
- Optionally share tokens with your team: Postman makes it easier to collaborate on OAuth APIs by enabling users to easily share token credentials with other members of their team. Note that Postman won’t share your tokens with your team members unless you specifically tell it to.