OIDC Integration Guide
Detailed walkthrough of integrating Trousseau authentication using the Authorization Code flow with PKCE.
Overview
Trousseau implements the OpenID Connect Authorization Code flow with PKCE — the most secure standard flow for web and mobile applications.
This guide walks you through the complete flow, from redirecting the user to Trousseau to handling tokens and refreshing sessions.
Flow diagram
Your App Trousseau (IdP)
│ │
│ 1. Generate PKCE verifier │
│ + code_challenge │
│ │
│ 2. Redirect to /authorize │
│ ─────────────────────────────> │
│ │
│ │ 3. User authenticates
│ │ (email + password + MFA)
│ │
│ 4. Redirect to callback │
│ with ?code=xxx │
│ <───────────────────────────── │
│ │
│ 5. POST /token │
│ code + code_verifier │
│ ─────────────────────────────> │
│ │
│ 6. Receive tokens │
│ (id_token, access_token, │
│ refresh_token) │
│ <───────────────────────────── │
│ │
│ 7. Validate id_token │
│ 8. Create app session │Endpoints
All endpoints can be discovered via the OpenID Configuration URL:
GET https://auth.keysuite.app/application/o/{your-slug}/.well-known/openid-configuration| Endpoint | URL |
|---|---|
| Authorization | https://auth.keysuite.app/application/o/authorize/ |
| Token | https://auth.keysuite.app/application/o/token/ |
| UserInfo | https://auth.keysuite.app/application/o/userinfo/ |
| JWKS | https://auth.keysuite.app/application/o/{your-slug}/jwks/ |
| End Session | https://auth.keysuite.app/application/o/{your-slug}/end-session/ |
Step 1: Authorization request
Redirect the user to the authorization endpoint with these parameters:
GET https://auth.keysuite.app/application/o/authorize/
?client_id={your-client-id}
&response_type=code
&scope=openid email profile
&redirect_uri=https://app.yourapp.com/auth/callback
&state={random-state-value}
&code_challenge={code-challenge}
&code_challenge_method=S256| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Your OIDC client ID |
response_type | Yes | Always code |
scope | Yes | Space-separated scopes (openid is required) |
redirect_uri | Yes | Must match a registered redirect URI |
state | Recommended | Random string to prevent CSRF attacks |
code_challenge | Yes | PKCE challenge (SHA-256 of the code verifier, base64url-encoded) |
code_challenge_method | Yes | Always S256 |
PKCE generation
// Generate a random code verifier
const codeVerifier = crypto.randomUUID() + crypto.randomUUID();
// Create the code challenge (SHA-256 hash, base64url-encoded)
const encoder = new TextEncoder();
const digest = await crypto.subtle.digest("SHA-256", encoder.encode(codeVerifier));
const codeChallenge = btoa(String.fromCharCode(...new Uint8Array(digest)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");PKCE is mandatory. Trousseau rejects authorization requests without a valid code_challenge. This protects against authorization code interception attacks.
Step 2: User authentication
Trousseau displays its login page. The user authenticates with their email and password. If MFA is configured, they are prompted for their second factor.
First-time users (no password yet) are automatically directed to a password setup page. This is transparent to your application — you always receive the same callback.
Step 3: Authorization callback
After successful authentication, Trousseau redirects to your redirect_uri:
GET https://app.yourapp.com/auth/callback
?code=abc123
&state={your-state-value}Always verify that the state parameter matches the value you sent in Step 1.
Step 4: Token exchange
Exchange the authorization code for tokens:
POST https://auth.keysuite.app/application/o/token/
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=abc123
&redirect_uri=https://app.yourapp.com/auth/callback
&client_id={your-client-id}
&client_secret={your-client-secret}
&code_verifier={code-verifier-from-step-1}Response:
{
"access_token": "eyJhbGciOi...",
"token_type": "Bearer",
"expires_in": 300,
"refresh_token": "eyJhbGciOi...",
"id_token": "eyJhbGciOi...",
"scope": "openid email profile"
}Step 5: Validate the ID token
The id_token is a JWT containing the user's claims. Before trusting it:
- Verify the signature using the JWKS endpoint
- Check
issmatches your issuer URL - Check
audmatches your client ID - Check
expis in the future - Check
nonceif you sent one (optional)
Most OIDC libraries handle this validation automatically.
Decoded ID token example
{
"iss": "https://auth.keysuite.app/application/o/your-slug/",
"sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"aud": "your-slug-oidc",
"exp": 1712400000,
"iat": 1712399700,
"email": "jean.dupont@hotel.com",
"email_verified": true,
"name": "Jean Dupont",
"given_name": "Jean",
"family_name": "Dupont",
"picture": "https://auth.keysuite.app/media/avatars/..."
}Step 6: Refresh tokens
Access tokens expire after 5 minutes. Use the refresh token to obtain new ones without requiring the user to re-authenticate:
POST https://auth.keysuite.app/application/o/token/
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token
&refresh_token={refresh-token}
&client_id={your-client-id}
&client_secret={your-client-secret}Response: Same format as the token exchange response, with new tokens.
Refresh tokens are valid for 30 days. After expiry, the user must re-authenticate via the authorization flow.
Error handling
Authorization errors
If authentication fails or the user denies access, Trousseau redirects to your callback with an error:
GET https://app.yourapp.com/auth/callback
?error=access_denied
&error_description=User denied access
&state={your-state-value}Common error codes:
| Error | Meaning |
|---|---|
access_denied | User is not in the access group for your application |
invalid_scope | You requested a scope that is not allowed for your app |
server_error | Internal error — retry or contact support |
Token errors
| Error | Meaning |
|---|---|
invalid_grant | Authorization code expired or already used |
invalid_client | Wrong client ID or secret |
invalid_request | Missing required parameter |