Integrating Single Sign-On with OAuth 2.0 PKCE Flow
Introduction
Single Sign-On (SSO) is a way for users to log into multiple services with just one set of credentials. Having a smooth and secure user experience is crucial and makes a significant difference.
Especially in large companies with many employees, SSO greatly simplifies the task of managing and granting access rights. It reduces the complexities of user management and ensures that the right people have the right access at the right time.
What is PKCE?
PKCE (pixie), or “Proof Key for Code Exchange”, is a security measure introduced to enhance the protection of SSO systems. At its core, PKCE provides an extra layer of security by introducing a unique, one-time code known as the “code challenge”. This ensures that even if an attacker intercepts our temporary authorization code, they can’t misuse it without the original challenge.
Initially designed for mobile apps due to their inability to securely store client secrets, PKCE later found its importance for all OAuth clients. It effectively guards against unauthorized code use. While some might mistakenly consider PKCE a replacement for client secrets, it is, in fact, a complementary security layer. This means it remains advantageous even for applications that have a client secret, fortifying them against potential threats.
Beneath the PKCE mechanism lie two critical components: the code_challenge
and the code_verifier
. Let's break down how they work in tandem with the flow diagram above:
code_verifier
: This is a random set of letters and numbers created by the client. It is like a secret password.code_challenge
: The client transforms thecode_verifier
using a specific method to produce thecode_challenge
. The widely accepted method for this transformation issha256
. Once created, thecode_challenge
is sent to the authorization server during the initial request.SHA256
: This is a cryptographic hash function, part of the SHA-2 family. By usingsha256
, the client ensures that thecode_challenge
sent to the server is a secure version of the originalcode_verifier
.
The clever thing is that the real code_verifier
(our original secret password) is never directly shared. Later, when the client asks for a token to enter, it shares this original secret with the server. The server checks it by comparing it with the code_challenge
it got before. This way, only the correct client can get access.
PKCE Flow
1- Login Initialization
The user clicks the login button. In the background, the app generates a long random string called the code_verifier. This is like a secret password that only the app knows. Then, the app makes a scrambled version of this, called the code_challenge
2- Login to the Identity Provider
The application now reroutes the user to IDP’s login page (This could be OneLogin or Okta). The user enters their username and password to log in to IDP.
3- Credential Verification
Upon successful verification, the identity provider acknowledges the code_challenge provided by the application earlier.
4- Return with Authorization Code
The user is then redirected back to the originating application. During this phase, the identity provider hands the application a special ‘authorization code’, a crucial component of this secure flow.
5- Code Exchange at the IDP Server
The application communicates with the identity provider’s server, presenting the ‘authorization code’ alongside the initially generated code_verifier. The IDP server validates whether the code_verifier, when processed, matches the code_challenge received initially.
6- Access Token Procurement
Upon successful validation, the IDP server issues tokens to the application. This often includes an access token, which grants the user access. Depending on IDP settings, it might also provide an ID token, which the application can use to identify which user has logged in, and a refresh token, which can be used to obtain a new access token once the original has expired or been revoked.
7- Token Verification
Importantly, the integrity of this access token can be independently verified by the application’s server, ensuring its authenticity. The commendable aspect of the PKCE method lies in its ability to authenticate securely without the application storing any sensitive client secrets, bolstering overall security.
Integration
Incorporating Single Sign-On (SSO) with PKCE in applications is streamlined thanks to packages like js-pkce
. The package offers a set of tools that manage the creation and validation of the required PKCE parameters, thus facilitating the integration process.
1- Create New PKCE Instance:
import PKCE from 'js-pkce';
const pkce = new PKCE({
client_id: 'CLIENT_ID_FROM_IDP',
redirect_uri: 'http://localhost:8080/auth',
authorization_endpoint: 'https://authserver.com/oauth/authorize',
token_endpoint: 'https://authserver.com/oauth/token',
requested_scopes: 'openid',
});
2- Redirect the user to the login page of IDP to login
window.location.replace(pkce.authorizeUrl());
3- Trade the auth-code for a token
const url = window.location.href;
pkce.exchangeForAccessToken(url).then((resp) => {
const accessToken = resp.access_token;
const idToken= resp.id_token;
// Do stuff with the tokens.
});
Advantages of PKCE
1- No Need for Private Information (Client Secret, Keys, Certificates, etc.)
In traditional OAuth flows, applications store a “client secret” for added security. However, on platforms like mobile devices or web browsers, keeping this secret can be risky because it might be exposed to threats. PKCE eliminates the need for such a secret, which is a major benefit for these platforms.
2- Automatic Attack Detection
PKCE introduces specific values like code_verifier
which are used during the authentication process. If an attacker tries to misuse an authorization code and they don't have the corresponding code_verifier
, the IDP server will recognize this mismatch. This way, any unauthorized attempts can be quickly detected and stopped.
3- Optional Backend Requirement
With PKCE, applications have the flexibility to authenticate users without necessarily having a backend server. This means developers can directly obtain user credentials without the need for server-side processing. This is especially beneficial for lightweight applications.
4- Simplified Integration for Multi-Tenant and Multi-IDP Configurations
If you’re integrating multiple Identity Providers (IDPs) into your application or operating in a multi-tenant mode where each tenant requires its own SSO integration, the PKCE approach simplifies this. For each integration, you only need to specify the Client ID and the . Well-Known JSON URL.
Disadvantages of PKCE
1- Content Security Policy Restrictions
If a system is using a Content Security Policy (CSP), permissions need to be given explicitly for each Identity Provider (IDP). This means that integrating multiple IDPs can complicate CSP management and require more frequent updates.
2- Complexity
Incorporating PKCE adds some complexity to the initial OAuth 2.0 setup. Developers need to understand the new parameters like code_verifier
and code_challenge
, and how they interact in the flow.
3- Potential Integration Issues
Not all OAuth 2.0 providers may support PKCE out of the box. While its adoption is growing, developers might face challenges integrating PKCE with certain older systems or providers.
Summary
PKCE, introduced in 2015, is a newer security measure designed to enhance the safety of the OAuth 2.0 protocol’s Authorization Code flow. PKCE’s main strength lies in its ability to prevent unauthorized code use. Utilizing elements like the code_verifier
and code_challenge
, ensures a secure and robust authentication process.
With PKCE is that there’s no need to store any confidential information, such as secret keys. This means the IDP (Identity Provider) takes on almost all of the security concerns. In conclusion, PKCE is a method that balances both security and flexibility.