Table of Contents

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_id
  • client_secret
  • One or more allowed redirect_uri values (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_endpoint
  • token_endpoint
  • jwks_uri
  • userinfo_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=code
  • client_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 state matches 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_code
  • client_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_token
  • id_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)
  • aud contains your client_id
  • token lifetime claims (e.g., exp)
  • nonce matches 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 sub claim in the UserInfo response must match the sub in 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 Bearer access token.