Secure SPA with Keycloak PKCE Authorization Code Flow
Single-page JavaScript applications, in particular, face a fundamental challenge in ensuring the secret key confidentially. Developers can easily access and expose the client’s secret key by inspecting the source code. Because a JavaScript page cannot ensure the confidentiality of a secret key, it cannot rely on this traditional method of authentication. Instead, it employs an alternative approach by generating its secret key in a manner that the authorization server can validate. This process is known as Proof Key for Code Exchange (PKCE).
Introduction
PKCE is a security extension for OAuth 2.0 authorization code flow. It was designed to enhance the security of authorization code flow, especially for public clients like single-page applications (SPAs) and mobile apps.
The PKCE Flow
In the PKCE-enhanced authorization code grant flow, the client application must generate and include two additional request parameters in its initial request to the authorization server: code_challenge
and code_challenge_method
.
The client (usually a mobile app or SPA) initiates the OAuth 2.0 authorization code flow by redirecting the user to the authorization server’s authorization endpoint. Along with the request, the client generates a random secret value called the code_verifier
. This secret value is used to protect the authorization code exchange process.
The client creates a code_challenge
by hashing the code_verifier
. This hash can use various methods, but the most commonly used is the SHA-256 algorithm. Next, the client includes the code_challenge
and specifies the hashing method (e.g., “S256”) in the authorization request using the code_challenge_method
parameter.
The user is presented with the authentication and consent screen by the authorization server. The user logs in and grants permissions to the client. After successful authentication and authorization, the authorization server issues an authorization code to the client.
The Exchange Process
Now, instead of directly exchanging the authorization code for an access token, the client initiates a token exchange request. The client sends a POST request to the token endpoint with the following parameters:
grant_type
: Set to authorization_code.code
: The authorization code received in the previous step.code_verifier
: The original secret value used to create thecode_challenge
.
The authorization server validates the authorization code and the code_verifier
. If the validation is successful, the authorization server responds with an access token, an optional refresh token, and other relevant data.
Why need PKCE
The Authorization Code Flow is a secure and widely used method for obtaining user authorization and access tokens in OAuth 2.0. However, it has a potential security vulnerability known as the Authorization Code Interception attack, which can occur when the authorization code is intercepted by malicious actors during the redirection from the authorization server to the client application.
In the traditional Authorization Code Flow, the client (usually a web application) initiates the authorization process by redirecting the user to the authorization server for authentication. After successful user authentication and authorization, the authorization server redirects the user back to the client application with an authorization code as a query parameter. The client application exchanges this authorization code for an access token and, potentially, a refresh token.
The Potential Vulnerability
When the authorization code is passed in the URL as a query parameter from the authorization server to the client. It’s possible for an attacker to intercept the authorization code by various means, such as browser history, logs, or other vulnerabilities before the client application can use it to obtain an access token. The attacker can then use the intercepted authorization code to impersonate the user and obtain an access token, gaining unauthorized access to the user’s resources.
How PKCE Resolves the Vulnerability
PKCE ensures that even if an attacker intercepts the authorization code, they won’t be able to use it without knowing the original code verifier. When the client initiates the authorization request, it generates a random value called a code verifier. This code verifier is kept secret and isn’t included in the URL or exposed. The client uses the code verifier to create a code challenge value. This value is derived from the code verifier through a transformation, typically a SHA-256 hash, followed by base64 encoding. The code challenge is included in the authorization request to the authorization server.
The authorization server stores the code challenge and its transformation method (PKCE method) during the initial authorization request. When the client exchanges the authorization code for an access token, it must also provide the code verifier. The authorization server compares the code challenge provided during the token exchange with the previously stored code challenge. If they match, the authorization code exchange is successful. If not, the request is rejected.
Setting Up Keycloak Client with PKCE
This process is essential to ensure that your application can leverage the benefits of PKCE for secure authorization.
- Navigate to the
Clients
section and create a new client or select an existing one that we want to enable with PKCE. - Under the
Advanced
tab for the selected client, selectS256
for the Proof Key for Code Exchange Challenge Method option. - Save the client configuration to apply the PKCE setting.
Updating React Application to Use PKCE
In our previous React project, ensured that the pkceMethod
is set to S256
in the keycloak.init()
. This aligns with the code challenge method we configured in Keycloak.
keycloak
.init({
pkceMethod: "S256",
onLoad: "check-sso",
checkLoginIframe: false,
})
.then((authenticated) => {
console.log(authenticated);
root.render(
<React.StrictMode>
<KeycloakContext.Provider value={keycloak}>
<App />
</KeycloakContext.Provider>
</React.StrictMode>
);
});
Start the react application, you will notice the authentication request parameter has been changed. Keycloak’s library, when configured with PKCE, will generate the code verifier and code challenge as required by the PKCE flow.
After login, when requesting the token, we also see the code_verifier was added.
Conclusion
In this post, we learned what is PKCE and how to secure a React web app using Keycloak PKCE. PKCE enhances the security of the Authorization Code Flow by protecting the authorization code from interception, providing an additional layer of security against authorization code attacks. This is particularly important for public clients like single-page JavaScript applications that can’t safely store client secrets.
Share this content:
Leave a Comment