OpenID Connect (OIDC) Integration Guide
This guide describes how to integrate this OAuth2 service into your application as an OIDC Provider.
1. Discovery Endpoint
The OIDC discovery endpoint provides all configuration information about the Provider:
GET /.well-known/openid-configuration
Example response:
{
"issuer": "https://your-domain.com",
"authorization_endpoint": "https://your-domain.com/oauth2/authorize",
"token_endpoint": "https://your-domain.com/oauth2/token",
"userinfo_endpoint": "https://your-domain.com/oauth2/userinfo",
"jwks_uri": "https://your-domain.com/.well-known/jwks.json",
"response_types_supported": ["code"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["openid", "profile", "email"]
}
2. JWKS Endpoint
The JSON Web Key Set endpoint provides the public keys used to verify id_token signatures:
GET /.well-known/jwks.json
Example response:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "default-key-id",
"n": "<base64url-encoded modulus>",
"e": "AQAB"
}
]
}
3. id_token Format
The id_token is an RS256-signed JWT containing the following standard claims:
| Claim | Description | Example |
|---|---|---|
iss | Issuer | https://your-domain.com |
sub | Unique user identifier (UUID public_sub) | 550e8400-e29b-41d4-a716-446655440000 |
aud | Audience (Client ID) | your-client-id |
exp | Expiration time (Unix timestamp) | 1700000000 |
iat | Issued-at time (Unix timestamp) | 1699996400 |
nonce | The nonce value sent in the request | abc123 |
Depending on the requested scopes, it may also contain:
- profile scope:
name,preferred_username - email scope:
email,email_verified
4. Verifying the id_token
4.1 Verification Steps
- Decode the JWT header: extract
kid(Key ID) andalg(should be RS256) - Fetch the public key: retrieve the public key matching the
kidfrom the JWKS endpoint - Verify the signature: verify the JWT signature with the RSA public key
- Verify the claims:
issmust match your configured Issuer URLaudmust include your Client IDexpmust be in the futurenoncemust match the value you sent in the authorization request
4.2 Security Considerations
- Always verify the signature; never trust an unverified JWT
- Cache the JWKS, but with a sensible refresh interval (24 hours is recommended, or follow the Cache-Control header)
- Check the
algheader and reject thenonealgorithm (prevents algorithm downgrade attacks)
5. Supported Scopes and Claims
| Scope | Returned Claims |
|---|---|
openid | sub (required scope; enables OIDC) |
profile | name, preferred_username |
email | email, email_verified |
6. Integration Examples
6.1 Using a Standard OIDC Client Library (Node.js)
const { Issuer } = require('openid-client');
// Discover the Provider configuration automatically
const issuer = await Issuer.discover('https://your-domain.com');
const client = new issuer.Client({
client_id: 'your-client-id',
client_secret: 'your-client-secret',
redirect_uris: ['http://localhost:3000/callback'],
response_types: ['code'],
});
// Generate the authorization URL
const authUrl = client.authorizationUrl({
scope: 'openid profile email',
state: 'random-state-value',
nonce: 'random-nonce-value',
});
// Handle the callback
const params = client.callbackParams(req);
const tokenSet = await client.callback('http://localhost:3000/callback', params, {
state: 'random-state-value',
nonce: 'random-nonce-value',
});
console.log('ID Token claims:', tokenSet.claims());
console.log('Access Token:', tokenSet.access_token);
6.2 Using Python (authlib)
from authlib.integrations.requests_client import OAuth2Session
client = OAuth2Session(
client_id='your-client-id',
client_secret='your-client-secret',
redirect_uri='http://localhost:8000/callback',
scope='openid profile email'
)
# Build the authorization URL
uri, state = client.create_authorization_url(
'https://your-domain.com/oauth2/authorize'
)
# Handle the callback and exchange for tokens
token = client.fetch_token(
'https://your-domain.com/oauth2/token',
authorization_response=callback_url
)
# Fetch user information
userinfo = client.get('https://your-domain.com/oauth2/userinfo').json()
6.3 Using Go (coreos/go-oidc)
provider, err := oidc.NewProvider(ctx, "https://your-domain.com")
oauth2Config := oauth2.Config{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
RedirectURL: "http://localhost:8080/callback",
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
}
// Verify the id_token
verifier := provider.Verifier(&oidc.Config{ClientID: "your-client-id"})
idToken, err := verifier.Verify(ctx, rawIDToken)
7. FAQ
Q: How long is the id_token valid? A: The same as the access token — 1 hour by default.
Q: How should key rotation be handled? A: Refresh the public keys from the JWKS endpoint periodically. When verification fails, refresh the JWKS first, then retry the verification.
Q: Is PKCE supported?
A: Yes. PKCE (RFC 7636) is implemented and enforced by default for PUBLIC clients (both plain and S256 are supported). SPA and mobile clients should use S256.