Security
Learning Corner
Intermediate 12 min read

OpenID Connect (OIDC)

Modern identity layer on OAuth 2.0 — verify users across apps with ID tokens

Think of it as a

Digital Passport

issued by providers you already trust

The Airport Analogy

Imagine you're at an airport. You don't need to prove your identity to every shop, lounge, and gate separately. You show your passport once at security, get a boarding pass, and that pass grants you access everywhere. OIDC works the same way — one trusted authority verifies who you are, and everyone else trusts that verification.

YOU User w/ credentials SECURITY (Identity Provider) ID Token YOUR APPS (Relying Parties) App 1 App 2 API Mobile SaaS Admin login token
🧑

You (User)

Have credentials with IdP

🛂

Identity Provider

Verifies you, issues ID token

🏢

Relying Parties

Trust the token, know who you are

The Problem It Solves

Roll Your Own Auth
// Every app builds its own auth 😰

// App 1: Store passwords
users_table: email, password_hash, salt

// App 2: Different password format
accounts: username, bcrypt_hash

// App 3: Oh no, we got hacked
SELECT * FROM users; -- leaked!

// User has 47 passwords to remember
// Each app handles MFA differently
// No SSO between your own apps
  • Every app stores credentials separately
  • Password reuse across services
  • Each app implements MFA from scratch
  • No single sign-on experience
  • Security breaches affect one app at a time
OpenID Connect
// All apps trust one identity provider

// User clicks "Sign in with Google"
redirect → accounts.google.com

// Google handles auth, returns token
{
  "sub": "user-123",
  "email": "alex@example.com",
  "name": "Alex",
  "email_verified": true
}

// Your app trusts this. Done. ✓
  • One login for all connected apps
  • No passwords stored in your database
  • MFA handled by the identity provider
  • Standardized, audited, battle-tested
  • Users trust familiar providers

OAuth 2.0 vs OIDC

OIDC is built on top of OAuth 2.0. They're not competing standards — they solve different problems.

OAuth 2.0 "Can this app access my data?" Authorization Access Token → API access OpenID Connect "Who is this user?" Authentication + Identity ID Token → user info +

OAuth 2.0 alone

Grants access to resources. Doesn't tell you who the user is. Like a valet key — it opens the door, but doesn't identify the driver.

OIDC adds identity

Returns a standardized ID token with user info (sub, email, name). Now you know exactly who logged in.

Core Concepts

🎫

ID Token

A signed JWT containing user identity. Always a JWT, always signed by the provider. This is what makes OIDC different from OAuth.

📋

Claims

Key-value pairs inside the ID token. Standard claims: sub (user ID), email, name, iat (issued at).

🔑

Scopes

Define what info you're requesting. openid (required), profile, email, address, phone.

🏛️

Identity Provider (IdP)

The authority that authenticates users. Google, Okta, Auth0, Azure AD, AWS Cognito, or your own.

🏢

Relying Party (RP)

Your application. It "relies" on the IdP to verify users. Validates the ID token and extracts claims.

🔍

Discovery Document

JSON at /.well-known/openid-configuration listing all endpoints and supported features.

Authorization Code Flow (with PKCE)

The most secure flow for web apps and SPAs. PKCE (Proof Key for Code Exchange) protects against authorization code interception.

Browser Your App IdP 1. Click "Login" 2. Generate PKCE 3. Redirect + code_challenge 4. User logs in 5. Redirect + auth code 6. code + code_verifier 7. ID token + access token 8. Validate tokens 9. Session created ✓
code_verifier

Random secret, kept by your app

code_challenge

Hash of verifier, sent to IdP

auth code

One-time code, exchanged for tokens

🔐

What is PKCE?

Proof Key for Code Exchange — protection against authorization code interception attacks.

Without PKCE, if an attacker intercepts the authorization code (via malware, logs, or network sniffing), they could exchange it for tokens. PKCE prevents this by binding the code to the original requestor.

// 1. Generate a random code_verifier (43-128 chars)
const code_verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"

// 2. Create code_challenge = BASE64URL(SHA256(code_verifier))
const code_challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"

// 3. Send code_challenge in authorize request
// 4. Send code_verifier in token exchange
// 5. IdP verifies: SHA256(code_verifier) === code_challenge

// Attacker has the code but not the verifier → blocked!
  • Originally designed for mobile/native apps (public clients)
  • Now recommended for ALL clients, including server-side apps
  • S256 method (SHA-256) is required; plain method is deprecated

Discovery Document

Every OIDC provider exposes a /.well-known/openid-configuration endpoint. This JSON document tells your app everything it needs to know — no hardcoding URLs.

// GET https://accounts.google.com/.well-known/openid-configuration
{
  "issuer": "https://accounts.google.com",
  "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
  "token_endpoint": "https://oauth2.googleapis.com/token",
  "userinfo_endpoint": "https://openidconnect.googleapis.com/v1/userinfo",
  "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
  
  "scopes_supported": ["openid", "email", "profile"],
  "response_types_supported": ["code", "token", "id_token"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  
  "id_token_signing_alg_values_supported": ["RS256"],
  "code_challenge_methods_supported": ["S256", "plain"]
}

Key Endpoints

  • authorization_endpoint — where users log in
  • token_endpoint — exchange code for tokens
  • jwks_uri — public keys for token validation
  • userinfo_endpoint — fetch additional claims

Why Use Discovery?

  • Auto-configure your OIDC library
  • Endpoints can change — discovery stays current
  • Validate issuer matches token claims
  • Check supported features before using them

Inside an ID Token

An ID token is a JWT (JSON Web Token) with three parts: header, payload, and signature.

// Decoded ID Token

// Header
{
  "alg": "RS256",      // Signing algorithm
  "typ": "JWT",
  "kid": "abc123"       // Key ID for verification
}

// Payload (the claims)
{
  "iss": "https://accounts.google.com",  // Issuer
  "sub": "110248495921238986420",        // Subject (user ID)
  "aud": "your-client-id.apps.google",  // Audience (your app)
  "exp": 1707858400,                     // Expiration
  "iat": 1707854800,                     // Issued at
  "nonce": "abc123xyz",                   // Replay protection
  
  // Standard profile claims
  "email": "alex@example.com",
  "email_verified": true,
  "name": "Alex",
  "picture": "https://..."
}

// Signature
RSASHA256(base64(header) + "." + base64(payload), privateKey)

✅ Always Validate

  • Signature matches (use IdP's public keys)
  • iss matches expected issuer
  • aud matches your client ID
  • exp hasn't passed
  • nonce matches (if you sent one)

❌ Never Trust Blindly

  • Don't decode without verifying signature
  • Don't accept tokens from wrong issuer
  • Don't skip expiration checks
  • Don't use tokens from URL fragments in logs

Provider Integrations

🟠

AWS Cognito

Managed user pools with built-in OIDC. Great for AWS-native apps.

# Discovery endpoint
https://cognito-idp.{region}.amazonaws.com/{userPoolId}/.well-known/openid-configuration

# Example authorize URL
https://{domain}.auth.{region}.amazoncognito.com/oauth2/authorize?
  client_id=your-client-id&
  response_type=code&
  scope=openid email profile&
  redirect_uri=https://yourapp.com/callback&
  code_challenge={challenge}&
  code_challenge_method=S256
  • User Pools handle registration, login, MFA
  • Hosted UI or custom UI with SDK
  • Integrates with API Gateway, ALB, AppSync
  • Supports social providers (Google, Facebook, Apple)

SDK Examples

1

Install AWS Amplify

npm install aws-amplify
2

Configure Amplify

import { Amplify } from 'aws-amplify';

Amplify.configure({
  Auth: {
    Cognito: {
      userPoolId: 'us-east-1_xxxxx',
      userPoolClientId: 'your-client-id',
      loginWith: {
        oauth: {
          domain: 'your-domain.auth.us-east-1.amazoncognito.com',
          scopes: ['openid', 'email', 'profile'],
          redirectSignIn: ['https://yourapp.com/callback'],
          redirectSignOut: ['https://yourapp.com/'],
          responseType: 'code',  // PKCE enabled by default
        }
      }
    }
  }
});
3

Sign In & Get User

import { signInWithRedirect, getCurrentUser, fetchAuthSession } from 'aws-amplify/auth';

// Trigger login (redirects to Cognito Hosted UI)
async function login() {
  await signInWithRedirect();
}

// Get current user after callback
async function getUser() {
  const user = await getCurrentUser();
  const session = await fetchAuthSession();
  
  console.log(user.username);              // Cognito username
  console.log(session.tokens.idToken);     // JWT ID token
  console.log(session.tokens.accessToken); // JWT access token
}
4

React Hook Example

import { useAuthenticator } from '@aws-amplify/ui-react';

function Profile() {
  const { user, signOut } = useAuthenticator();
  
  return (
    <div>
      <p>Welcome, {user.username}</p>
      <button onClick={signOut}>Sign Out</button>
    </div>
  );
}

When to Use OIDC

Use when:

  • You need user authentication (who is this?)
  • You want single sign-on across multiple apps
  • Users should log in with existing accounts (Google, Microsoft)
  • You're building a B2B SaaS with enterprise SSO
  • You don't want to store passwords
  • You need standardized identity across platforms

⚠️ Consider alternatives if:

  • You only need API authorization (use OAuth 2.0 alone)
  • Simple API key auth is sufficient
  • It's a machine-to-machine flow (use client credentials)
  • You're building an internal tool with existing LDAP/AD
  • Your users are all anonymous

Alternatives to OIDC

📜

SAML 2.0

Enterprise

XML-based SSO standard from 2005. Still dominant in enterprise identity (Okta, Azure AD, PingFederate).

✓ Mature, widely supported ✓ Enterprise compliance − XML verbose − Browser-only
🔓

OAuth 2.0 (without OIDC)

API Access

Authorization only — grants API access without revealing user identity. Use when you need "can they access this?" not "who are they?"

✓ Simpler if no identity needed ✓ Scoped API access − No standard user info
🔑

API Keys

Service-to-Service

Simple static tokens. Best for server-to-server calls, developer APIs, or low-security internal tools.

✓ Dead simple ✓ No expiration headaches − No user context − Hard to rotate
✉️

Magic Links

Passwordless

Email a one-time login link. Great UX for infrequent users. No password to remember or steal.

✓ No passwords ✓ Simple UX − Email delivery delays − Email account = keys
🗝️

Passkeys / WebAuthn

Passwordless

Cryptographic keys stored on device, verified by biometrics. The future of auth — phishing-resistant, no shared secrets.

✓ Phishing-proof ✓ Great UX − Device-bound − Adoption still growing

Pro tip: These aren't mutually exclusive. Many apps combine OIDC for user login with API keys for service accounts, or offer passkeys as an alternative to passwords within an OIDC flow.

Trade-offs

Pros

  • Standardized protocol — libraries exist for everything
  • Outsource auth complexity to experts
  • Users get SSO and familiar login flows
  • Strong security with PKCE, signed tokens
  • Discovery documents make integration easy

Cons

  • Dependency on external identity provider
  • Complexity if you need custom flows
  • Token size can be large with many claims
  • Debugging redirect flows is tricky
  • Cost scales with active users (managed IdPs)

Key Takeaways

1

OIDC = OAuth 2.0 + Identity

OAuth tells you what the user can access. OIDC tells you who they are. The ID token is the key difference.

2

Always Use PKCE

Even for confidential clients. PKCE protects the authorization code exchange and is now best practice for all flows.

3

Validate Tokens Properly

Verify the signature, check issuer and audience, confirm expiration. Never trust a token without validation.

4

Use Well-Known Discovery

The /.well-known/openid-configuration endpoint tells you everything about the IdP — endpoints, supported scopes, algorithms.

5

Pick the Right Provider

Cognito for AWS-native apps, Okta for enterprise SSO. Or run your own with Keycloak.