OpenID Connect Authorization Code Flow with PKCE — Developer Integration Guide
This guide describes how to integrate an application (web, SPA, mobile, rich client, or backend) with PhenixID Authentication Services (PAS) using OpenID Connect Authorization Code Flow with PKCE.
Prerequisites
You need the following from the PAS administrator:
client_idclient_secret- One or more allowed
redirect_urivalues (exact match) - Tenant / base URL to the PAS OIDC endpoints (or use discovery, see below)
PAS provides at minimum:
- an authorization endpoint (for the user login/consent step)
- a token endpoint (to exchange the authorization code for tokens)
Authorization Code Flow Sequence
sequenceDiagram
participant UserAgent
participant RP as Relying Party (Client)
participant OP as OpenID Provider (PAS)
UserAgent ->>+ RP: 1. User initiates login
RP ->>+ OP: 2. Redirect to Authorization Endpoint
OP ->>+ UserAgent: 3. Prompt user for authentication
UserAgent ->>+ OP: 4. User authenticates
OP -->> UserAgent: 5. Redirect with Authorization Code
UserAgent ->>+ RP: 6. Authorization Code sent to RP
RP ->>+ OP: 7. Request tokens at Token Endpoint
OP -->> RP: 8. Respond with ID Token and Access Token
RP -->> UserAgent: 9. User gains access to RP
Discover endpoints
Use OIDC discovery to find the correct endpoints for your tenant/environment.
Example (pattern varies by deployment; discovery URL is exposed by PAS):
/<tenant>/.well-known/openid-configuration
From discovery you will obtain:
authorization_endpointtoken_endpointjwks_uriuserinfo_endpoint
Generate PKCE values
Generate code_verifier
Create a cryptographically random string and store it client-side (or in your server-side session) for this login attempt.
Example (pseudo):
code_verifier = crypto_random()
Derive code_challenge
Hash the verifier with SHA-256 and Base64URL-encode the result:
code_challenge = BASE64URL( SHA256(code_verifier) )
The supported methods are advertised in the discovery document. The plain method should only be used as a fallback for legacy providers that do not support S256.
Use code_challenge_method=S256 (recommended).
Redirect the user to the Authorization Endpoint
Redirect the user agent (browser/system browser) to the PAS authorization_endpoint with parameters similar to:
response_type=codeclient_id=...redirect_uri=...scope=openid ...state=...(CSRF protection; validate later)nonce=...(binds the login to the ID token; validate later)code_challenge=...code_challenge_method=S256
PAS supports returning response parameters in different modes (for example query or form_post). The supported response parameters are advertised in the discovery document.
Example redirect URL (query mode)
GET https://pas.example.com/authentication/oidc/t1/login
?response_type=code
&client_id=your_client_id
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback
&scope=openid%20profile
&state=YOUR_STATE
&nonce=YOUR_NONCE
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256
Example redirect URL (form_post mode)
If you want PAS to deliver the authorization response via an HTTP POST to your redirect_uri, add:
response_mode=form_post
GET https://pas.example.com/authentication/oidc/t1/login
?response_type=code
&client_id=your_client_id
&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback
&scope=openid%20profile
&response_mode=form_post
&state=YOUR_STATE
&nonce=YOUR_NONCE
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256
Handle the callback (authorization response)
After successful authentication, PAS issues an authorization code and redirects back to your redirect_uri.
Example callback redirect (query mode)
HTTP/1.1 302 Found
Location: https://client.example.com/callback?code=AUTH_CODE&state=YOUR_STATE
Example callback delivery (form_post mode)
PAS sends an HTTP POST to your redirect_uri containing form fields such as code and state.
In your callback handler:
- Validate
statematches what you stored before redirect. - Extract
code.
Exchange the authorization code for tokens (Token Endpoint)
Call the PAS token_endpoint using application/x-www-form-urlencoded.
Required parameters:
grant_type=authorization_codeclient_id=...code=...redirect_uri=...code_verifier=...
cURL example (token request)
curl -sS -X POST "https://pas.example.com/authentication/oidc/t1/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-u "client_id:client_secret" \
--data-urlencode "grant_type=authorization_code" \
--data-urlencode "client_id=your_client_id" \
--data-urlencode "code=AUTH_CODE_FROM_CALLBACK" \
--data-urlencode "redirect_uri=https://client.example.com/callback" \
--data-urlencode "code_verifier=YOUR_CODE_VERIFIER"
Successful response
If the authorization code and code_verifier are valid, the token endpoint returns a JSON response containing the issued tokens.
access_tokenid_token- (optionally)
refresh_token
Example successful response
{
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjBkOGNkaUFrNWNyNzhCRDNLclZjOXVFeGtMdyJ9.<truncated>",
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6IjBkOGNkaUFrNWNyNzhCRDNLclZjOXVFeGtMdyJ9.<truncated>",
"token_type": "Bearer",
"expires_in": 1800,
"refresh_token": "cc7a1706-b509-4704-9c11-c25d689023bc"
}
Response fields
| Field | Description |
|---|---|
id_token |
A signed JWT containing authentication information about the user session. Must be validated by the client. |
access_token |
JWT access token used to access APIs or the UserInfo endpoint. |
refresh_token |
Token used to obtain new access tokens without re-authentication (if enabled). |
token_type |
Typically Bearer. |
expires_in |
Access token lifetime in seconds (e.g. 1800 = 30 minutes). |
Validate the ID Token (JWT)
After token exchange, validate the returned id_token as a signed JWT using the OP’s keys (jwks_uri) from discovery.
Example id_token (JWT Payload)
{
"iss": "https://your-pas-domain.com/t1",
"sub": "user123456789",
"aud": "your_client_id",
"exp": 1924972800,
"iat": 1924972500,
"nbf": 1924972500,
"nonce": "randomNonceValue123456",
"azp": "your_client_id",
"sid": "4f32a1d2-98bf-44f3-8a1b-e4c5757d8c72",
"given_name": "Alice",
"family_name": "Anderson",
"email": "alice.anderson@example.com"
}
Explanation of Fields
| Claim | Meaning |
|---|---|
iss |
Issuer identifier — PAS OIDC Provider URL. |
sub |
Subject — unique identifier for the authenticated user. |
aud |
Audience — the client (RP) that requested the token. |
exp |
Expiration time (UNIX timestamp). |
iat |
Issued at time (UNIX timestamp). |
nbf |
Not valid before (UNIX timestamp). |
nonce |
OIDC nonce — must match the value sent in the authorization request. |
azp |
Authorized party — usually the client id. |
sid |
Session identifier for the authenticated session. |
given_name |
Example user profile claim (optional, if requested via scope=profile). |
family_name |
Example surname/profile claim. |
email |
Example email claim (optional, if requested and configured). |
At minimum, validate:
- signature using keys from
jwks_uri iss(issuer)audcontains yourclient_id- token lifetime claims (e.g.,
exp) noncematches the value you sent in the authorization request
(Optional) Call UserInfo endpoint
If enabled and you have an access_token, you can retrieve additional claims from userinfo_endpoint.
Example UserInfo Request
curl -sS https://your-pas-domain.com/t1/userinfo \
-H "Authorization: Bearer ACCESS_TOKEN"
Example UserInfo Response
{
"sub": "199403011845",
"name": "Tiffany Sjömenning",
"given_name": "Tiffany",
"family_name": "Sjömenning",
"preferred_username": "tsjomenning",
"email": "tisj@sweauth.com",
"email_verified": true,
"acr": "possessionorinherence",
"amr": ["face"],
"sid": "wtAZ5B283cEXnflu5bRjVfQSZybpOZYaf0P45T5StYKAy3MtbFHJ30hwhyXBYoOujeQDKw=="
}
UserInfo Response Fields
| Claim | Description |
|---|---|
sub |
Subject identifier. Must match the sub in the ID token. |
name |
Full display name of the user. |
given_name |
User’s first name. |
family_name |
User’s last name. |
preferred_username |
Username or login identifier. |
email |
User email address (if scope email was requested). |
email_verified |
Indicates whether the email has been verified. |
acr |
Authentication context class reference. |
amr |
Authentication methods used (e.g., face, pwd, otp). |
sid |
Session identifier associated with the authentication. |
Important Notes
The
subclaim in the UserInfo response must match thesubin the ID token.Claims returned depend on:
- Requested scopes
- Claim release configuration in PAS
- Attribute availability in the identity source
The UserInfo endpoint requires a valid
Beareraccess token.