# 1. Client redirects user's browser to /authorize.
https://auth.your-app.com/oidc/authorize?
response_type=code
&client_id=docs-portal
&redirect_uri=https://docs.example.com/oauth/callback
&scope=openid+email+profile
&state=<csrf-token>
&nonce=<id-token-binding>
&code_challenge=<S256(verifier)>
&code_challenge_method=S256
# 2. User logs in (Pylon's /login handles whichever method they use).
# Pylon redirects browser back to redirect_uri with ?code=...&state=...
# 3. Client exchanges code at /token.
POST /oidc/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<auth-code>
&redirect_uri=https://docs.example.com/oauth/callback
&client_id=docs-portal
&client_secret=shh-1234
&code_verifier=<original-PKCE-verifier>
# 200 OK
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"id_token": "<RS256-signed JWT>",
"scope": "openid email profile"
}
# 4. Client calls /userinfo with the access_token.
GET /oidc/userinfo
Authorization: Bearer <access_token>
# 200 OK
{
"sub": "user_abc123",
"email": "[email protected]",
"email_verified": true,
"name": "Alice Liddell"
}