This is an old revision of the document!
Table of Contents
OIDC SSO Architecture
Overview
This system implements Single Sign-On (SSO) using:
- OpenID Connect (OIDC)
- OAuth2 Authorization Code Flow
- PKCE (Proof Key for Code Exchange)
- JWT Access Token
- JWKS for signature verification
Components
| Component | Responsibility |
| SPA | Browser frontend |
| ServerA | Business API A |
| ServerB | Business API B |
| ServerSSO | Identity Provider (IdP) |
| JWKS Endpoint | Public keys for JWT verification |
Architecture
+-------------+
| User |
+------+------+
|
v
+-------------+
| SPA |
+------+------+
|
|
v
+------------------+
| ServerSSO |
| OIDC Provider |
+--------+---------+
|
|
v
+------------------+
| JWKS Endpoint |
+------------------+
^
|
|
+--------+---------+
| ServerA |
+------------------+
+------------------+
| ServerB |
+------------------+
Endpoints
ServerSSO
Authorization Endpoint:
GET /oauth2/authorize
Token Endpoint:
POST /oauth2/token
UserInfo Endpoint:
GET /userinfo
JWKS Endpoint:
GET /.well-known/jwks.json
OIDC Discovery:
GET /.well-known/openid-configuration
JWT Example
Header
{
"alg": "RS256",
"kid": "key-2026"
}
Payload
{
"iss": "https://sso.company.com",
"sub": "123456",
"aud": "spa-client",
"exp": 1800000000,
"iat": 1799990000,
"scope": "openid profile email"
}
PKCE
SPA Generates
Code Verifier
X7sJ8M9nK2Q...
Code Challenge
BASE64URL( SHA256(code_verifier) )
Example:
code_challenge=AbCdEf123
Scenario 1: User NOT Authenticated
Step 1 - User Opens SPA
https://app.company.com
SPA checks:
access_token exists ?
Result:
No
Step 2 - SPA Creates PKCE
Generate:
code_verifier code_challenge state nonce
Store:
sessionStorage
Step 3 - Redirect to SSO
SPA redirects browser:
GET /oauth2/authorize ?response_type=code &client_id=spa-client &redirect_uri=https://app.company.com/callback &scope=openid profile email &state=abc123 &nonce=xyz123 &code_challenge=ABC &code_challenge_method=S256
Step 4 - User Not Logged In
ServerSSO checks session:
SSO Session Exists?
Result:
No
Show login page.
Step 5 - User Login
User enters:
username password
ServerSSO validates credentials.
Step 6 - Create SSO Session
ServerSSO creates:
SSO Cookie
Example:
Set-Cookie: SSO_SESSION=abc123 HttpOnly Secure SameSite=None
Step 7 - Authorization Code
ServerSSO generates:
authorization_code
Redirect:
https://app.company.com/callback ?code=AUTH_CODE &state=abc123
Step 8 - SPA Validates State
SPA verifies:
returned state == stored state
Step 9 - Exchange Code
SPA calls:
POST /oauth2/token
Request:
grant_type=authorization_code code=AUTH_CODE client_id=spa-client code_verifier=X7sJ8M9nK2Q... redirect_uri=https://app.company.com/callback
Step 10 - SSO Validates PKCE
ServerSSO:
SHA256(code_verifier) == stored code_challenge
Valid:
Issue Tokens
Step 11 - Token Response
{
"access_token":"JWT",
"id_token":"JWT",
"expires_in":3600,
"token_type":"Bearer"
}
Step 12 - SPA Calls ServerA
Request:
GET /api/orders Authorization: Bearer JWT
Step 13 - ServerA Validates JWT
ServerA reads:
kid
from JWT header.
Step 14 - ServerA Gets JWKS
If key not cached:
GET /.well-known/jwks.json
Response:
{
"keys":[
{
"kid":"key-2026",
"kty":"RSA",
"alg":"RS256"
}
]
}
Step 15 - ServerA Verifies
Verify:
- Signature
- exp
- iss
- aud
If valid:
200 OK
Step 16 - SPA Calls ServerB
Same JWT:
Authorization: Bearer JWT
Step 17 - ServerB Verifies JWT
Using same JWKS.
User accesses both systems without another login.
Scenario 2: User Already Authenticated
Initial Condition
Browser already contains:
SSO_SESSION cookie
from previous login.
Step 1 - User Opens SPA
SPA has no access token.
Step 2 - Redirect to SSO
SPA redirects to:
/oauth2/authorize
Step 3 - SSO Checks Session
ServerSSO finds:
SSO_SESSION exists
User already authenticated.
Step 4 - Skip Login Screen
No username/password required.
Step 5 - Generate Authorization Code
ServerSSO immediately creates:
AUTH_CODE
Step 6 - Redirect Back
https://app.company.com/callback ?code=AUTH_CODE &state=abc123
Step 7 - Exchange Code
SPA calls:
POST /oauth2/token
with PKCE.
Step 8 - Receive JWT
{
"access_token":"JWT",
"id_token":"JWT"
}
Step 9 - Access ServerA
Authorization: Bearer JWT
ServerA validates JWT via JWKS.
Step 10 - Access ServerB
Same token:
Authorization: Bearer JWT
ServerB validates JWT via JWKS.
Result:
No login page shown. True Single Sign-On.
Sequence Diagram - Not Authenticated
User | | Open SPA v SPA | | Redirect v ServerSSO | | Login Page v User | | Credentials v ServerSSO | | Authorization Code v SPA | | Token Request + PKCE v ServerSSO | | JWT v SPA | | Bearer Token +-----> ServerA | +-----> ServerB
Sequence Diagram - Already Authenticated
User | | Open SPA v SPA | | Redirect v ServerSSO | | Existing SSO Session | | Authorization Code v SPA | | Exchange Code v ServerSSO | | JWT v SPA | +-----> ServerA | +-----> ServerB
Security Best Practices
- Always use Authorization Code Flow + PKCE
- Never use Implicit Flow
- Use RS256 or ES256
- Validate issuer (iss)
- Validate audience (aud)
- Validate expiration (exp)
- Validate nonce
- Validate state
- Cache JWKS keys
- Use HTTPS everywhere
- Store tokens in memory when possible
- Use short-lived access tokens
- Rotate signing keys regularly
Summary
Browser Authentication:
- SSO Session Cookie
API Authorization:
- JWT Access Token
Token Validation:
- JWKS
Login Flow:
- Authorization Code + PKCE
Single Sign-On:
- Managed by ServerSSO Session Cookie
Resource Servers:
- ServerA
- ServerB
Identity Provider:
- ServerSSO
</code>
