Implementing OpenID Connect (OIDC)

1. Understanding OIDC vs OAuth 2.0

AspectOAuth 2.0OIDC
PurposeAuthorization (delegated access)Authentication + identity
Tokenaccess_token (opaque to client)+ id_token (JWT about user)
ScopeAPI-specificAdds openid required
Endpoints/authorize, /token+ /userinfo, /discovery

2. Using ID Tokens

ClaimMeaning
issIssuer URL
subStable user identifier
audClient ID
exp / iatValidity window
nonceEchoes request nonce — replay protection
auth_timeWhen authentication occurred
acr / amrAuthentication Context Class / Methods References
at_hashHash of access_token (binding)

3. Understanding OIDC Flow

1. /authorize?scope=openid+profile+email&nonce=N&...
2. User authenticates
3. Redirect with code
4. POST /token → {access_token, id_token, refresh_token}
5. Verify id_token (iss, aud, nonce, sig)
6. Optional: GET /userinfo with access_token for extra claims
      

4. Requesting UserInfo Endpoint

Example: UserInfo response

GET /userinfo
Authorization: Bearer eyJ...

{
  "sub": "user_42",
  "email": "alice@example.com",
  "email_verified": true,
  "name": "Alice Smith",
  "picture": "https://cdn/avatars/42.png",
  "locale": "en-US"
}

5. Validating ID Tokens

CheckDetail
SignatureAgainst JWKS key matching kid
issMatches discovery issuer
audContains client_id
exp / nbfWithin validity, allow ±60s skew
nonceMatches stored value
azpEquals client_id (if present)
at_hashMatches hash of access_token

6. Using Standard Claims

ScopeClaims Returned
profilename, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, updated_at
emailemail, email_verified
addressaddress (structured)
phonephone_number, phone_number_verified
offline_accessIssue refresh_token

7. Implementing OIDC Discovery

Example: Discovery document

GET https://issuer/.well-known/openid-configuration

{
  "issuer": "https://issuer",
  "authorization_endpoint": "https://issuer/authorize",
  "token_endpoint": "https://issuer/token",
  "userinfo_endpoint": "https://issuer/userinfo",
  "jwks_uri": "https://issuer/.well-known/jwks.json",
  "end_session_endpoint": "https://issuer/logout",
  "response_types_supported": ["code"],
  "subject_types_supported": ["public", "pairwise"],
  "id_token_signing_alg_values_supported": ["RS256", "ES256"],
  "code_challenge_methods_supported": ["S256"]
}

8. Using OIDC Scopes

ScopeEffect
openidMUST be present — triggers OIDC behavior
profile / email / address / phoneAdd corresponding standard claims
offline_accessLong-lived refresh_token

9. Implementing Logout Flow

SpecDetail
RP-Initiated LogoutGET end_session_endpoint?id_token_hint=...&post_logout_redirect_uri=...&state=...
Local logoutClear app session/cookies
Global logoutOP clears SSO session + propagates to other RPs

10. Using Session Management

MechanismDetail
check_session_iframePolled hidden iframe for session state
session_stateReturned with auth response
postMessageiframe → parent: changed/unchanged/error
Status (2026)Mostly superseded by back/front-channel logout

11. Implementing Front-Channel Logout

AspectDetail
MechanismOP loads iframe of each RP's frontchannel_logout_uri
Requirementiframe-busting must be disabled for this URL
RiskBrowser third-party cookie restrictions can block

12. Implementing Back-Channel Logout

Example: Logout token (signed JWT from OP)

{
  "iss": "https://op.example",
  "aud": "client_123",
  "iat": 1715000000,
  "jti": "uuid",
  "sub": "user_42",
  "sid": "session-id",
  "events": { "http://schemas.openid.net/event/backchannel-logout": {} }
}
StepDetail
OP POSTSends logout_token to RP's backchannel_logout_uri
RP verifiesSignature, iss, aud, events, sid/sub
RP actionInvalidate sessions matching sub/sid
AdvantageServer-to-server — bypasses browser cookie issues