PKCE Flow
The Authorization Code Flow with PKCE (Proof Key for Code Exchange) is an extension of the Authorization Code Flow. It is specifically designed to authenticate native or mobile application users, but is recommended for all scenarios where user authentication is required, especially for public clients that cannot securely store a client_secret.
PKCE adds an extra layer of security by using a dynamically created cryptographic challenge instead of a static client secret. This makes it ideal for Single Page Applications (SPAs), mobile apps, and other public clients where storing secrets is not secure.
The primary difference between the PKCE flow and the standard Authorization Code flow is that PKCE doesn't require a client_secret. PKCE reduces security risks for native apps, as embedded secrets aren't required in the source code. This minimizes the exposure to reverse engineering security threats.
How it Works

Before we step into how it works, let's understand some useful parameters.
| Parameter Name | Description |
|---|---|
client_id | client which represents the business-App in cidaas, where the Resource Owner wants to have access |
redirect_uri | URI where cidaas should redirect to, after login. Must be provided in both authorization request and token exchange request, and must match in both calls |
scope | scope which the user asks for. E.g. profile gives access to profile data |
response_type | type of response. In PKCE Flow it has to be code |
state | random onetime string which will be generated at the beginning of the flow. Recommended to avoid CSRF |
code_challenge | A base64url-encoded SHA256 hash of the code_verifier. Sent in the authorization request |
code_challenge_method | The method used to generate the code challenge. Must be S256 for SHA256 |
grant_type | authorization_code needs to be mentioned in code-exchange call to get a token |
code | the one-time-use code must be provided as code parameter |
code_verifier | The code verifier is a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~, between 43 and 128 characters long. Must be sent in the token exchange request |
Step 1: The Client (your app) creates the code_verifier. (RFC 7636, Section 4.1)
Step 2: Client creates the code_challenge by transforming the code_verifier using SHA256 hashing and base64url encoding. (RFC 7636, Section 4.2)
Step 3: Client sends the code_challenge and code_challenge_method with the initial authorization request. The authorization request includes: response_type=code, redirect_uri (your redirect URI), client_id (the client_id of your application), scope (requested permissions), state (a random string for CSRF protection), code_challenge, and code_challenge_method=S256. (RFC 7636, Section 4.3)
The Authorization Endpoint initiates the login process. Understanding how to construct the authorization URL is crucial for implementing PKCE.

Step 4: The user will then enter their credentials on the login page and click submit. This step involves user authentication, which can be performed using password credentials or passwordless authentication methods such as Magic Link, Email/SMS OTP, or FIDO2.
Step 5: After successful authentication, cidaas will return a code to the provided redirect_uri in the Authz-Call. The client must verify whether the state returned by the authorization server (cidaas) is equal to the state passed in Step 3. (RFC 7636, Section 4.4)
Step 6: The Client then requests Access Token from Authorization Server. The client sends the one-time-use code as code, code_verifier, grant_type=authorization_code, client_id, and redirect_uri (must match the redirect_uri from Step 3 for security validation) to the token endpoint. Since PKCE is designed for public clients, this request can be made directly from the client application (no backend required). (RFC 7636, Section 4.5)
Step 7: cidaas transforms the code_verifier using the code_challenge_method from the initial authorization request and checks the result against the code_challenge. If the value of both strings match, then the server has verified that the requests came from the same client and will issue an access_token and optionally a refresh token. (RFC 7636, Section 4.6)
Security Note
Important: PKCE is specifically designed for public clients (SPAs, mobile apps) where you cannot securely store a
client_secret. Thecode_verifieris generated client-side and never stored on a server. This flow provides protection against authorization code interception attacks without requiring a client secret.
When to Use PKCE Flow
Use this flow when:
- You're building a Single Page Application (SPA)
- You're developing a mobile application (iOS, Android)
- You cannot securely store a
client_secret(public client)- You want the highest security for public clients
- You need to perform token exchange directly from the client application
If you have a traditional web application with a secure backend, consider using the Authorization Code Flow instead, which uses a
client_secretfor additional security.
Generating Code Verifier and Challenge
To implement PKCE, you need to generate a code_verifier and its corresponding code_challenge. Here's how to do it:
Step 1: Generate Code Verifier
Create a cryptographically random string between 43 and 128 characters using the characters A-Z, a-z, 0-9, and the punctuation characters -._~.
JavaScript Example:
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
const code_verifier = generateCodeVerifier();
Node.js Example:
const crypto = require('crypto');
const base64url = require('base64url');
// Generate random bytes and encode
const randomBytes = crypto.randomBytes(32);
const code_verifier = base64url.encode(randomBytes);
Important: Store the
code_verifiersecurely in your application (e.g., in memory or secure storage). You'll need it for the token exchange request.
Step 2: Generate Code Challenge
Create a SHA256 hash of the code_verifier and base64url encode it to create the code_challenge.
JavaScript Example:
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(hash);
}
const code_challenge = await generateCodeChallenge(code_verifier);
Node.js Example:
const crypto = require('crypto');
const base64url = require('base64url');
const hash = crypto.createHash('sha256').update(code_verifier).digest();
const code_challenge = base64url.encode(hash);
The code_challenge is what you send in the authorization request. The code_verifier is kept secret and only sent during token exchange.
Technical Integration
The following APIs are used in the PKCE flow:
| API | Description | Link to API |
|---|---|---|
| Step 3: Start Authorization | To perform an authorization request with code_challenge | View API |
| Step 4: Initiate Authentication | To start the user authentication (password or passwordless) | View API |
| Step 4: Perform Authentication | To finish the user authentication | View API |
| Step 4: Continue Login Process | To continue the authentication and receive the authorization code | View API |
| Step 6: Exchange Code | To exchange the code for a token using code_verifier | View API |
Complete Implementation Example
Here's a complete example showing how to implement the PKCE Flow:
Step 1-2: Generate Code Verifier and Challenge
// Generate code_verifier
function generateCodeVerifier() {
const array = new Uint8Array(32);
crypto.getRandomValues(array);
return base64UrlEncode(array);
}
// Generate code_challenge
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest('SHA-256', data);
return base64UrlEncode(hash);
}
const code_verifier = generateCodeVerifier();
const code_challenge = await generateCodeChallenge(code_verifier);
Step 3: Authorization Request
Construct the authorization URL with the code_challenge:
GET https://{{base_url}}/authz-srv/authz?client_id={{client_id}}&redirect_uri={{redirect_uri}}&response_type=code&scope={{scope}}&state={{random_state_string}}&code_challenge={{code_challenge}}&code_challenge_method=S256
Example URL:
https://your-domain.cidaas.de/authz-srv/authz?client_id=your-client-id&redirect_uri=https://your-app.com/callback&response_type=code&scope=openid profile&state=random-state-123&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
Step 5: Extract Authorization Code
After successful authentication, extract the code from the redirect URI:
https://your-app.com/callback?code=authorization-code-here&state=random-state-123
Step 6: Token Exchange (Client-Side)
Exchange the authorization code for tokens using the code_verifier:
POST https://{{base_url}}/token-srv/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&client_id={{client_id}}
&code={{authorization_code}}
&redirect_uri={{redirect_uri}}
&code_verifier={{code_verifier}}
cURL Example:
curl --location --request POST 'https://your-domain.cidaas.de/token-srv/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=authorization_code' \
--data-urlencode 'client_id=your-client-id' \
--data-urlencode 'code=authorization-code-from-callback' \
--data-urlencode 'redirect_uri=https://your-app.com/callback' \
--data-urlencode 'code_verifier=your-stored-code-verifier'
Note: Unlike the Authorization Code Flow, PKCE allows the token exchange to be performed directly from the client application (SPA, mobile app) without requiring a backend server, since no
client_secretis needed.
Need Support?
Please contact us directly on our support page