| name | oauth-oidc-implementer |
| description | Expert in implementing OAuth 2.0 and OpenID Connect (OIDC) authentication flows. Specializes in secure token handling, social login integration, API authorization, and identity provider configuration. Handles both client-side and server-side flows with security best practices. |
| version | 1.0.0 |
| metadata | {"category":"security","tags":["oauth","oidc","authentication","authorization","jwt","security"],"pairs-with":[{"skill":"modern-auth-2026","reason":"OAuth/OIDC is one auth mechanism alongside passkeys in the modern auth stack"},{"skill":"security-auditor","reason":"OAuth token handling and PKCE flow security require dedicated vulnerability scanning"},{"skill":"api-architect","reason":"API authentication design patterns depend on OAuth scope and token architecture"}]} |
OAuth/OIDC Implementer
Overview
Expert in implementing OAuth 2.0 and OpenID Connect (OIDC) authentication flows. Specializes in secure token handling, social login integration, API authorization, and identity provider configuration. Handles both client-side and server-side flows with security best practices.
When to Use
- Implementing "Login with Google/GitHub/etc." social login
- Setting up OAuth 2.0 for API authorization
- Configuring OIDC for enterprise SSO
- Designing token refresh and session management
- Implementing PKCE for mobile/SPA applications
- Securing API endpoints with JWT validation
- Integrating with identity providers (Auth0, Okta, Keycloak)
- Troubleshooting OAuth flow failures
Capabilities
OAuth 2.0 Flows
- Authorization Code flow (with PKCE)
- Client Credentials flow for service-to-service
- Implicit flow (legacy, understand why to avoid)
- Device Authorization flow for IoT
- Refresh token rotation
OpenID Connect
- ID token validation and claims
- UserInfo endpoint usage
- Discovery document (
.well-known/openid-configuration)
- Session management (front-channel/back-channel logout)
- PKCE for public clients
Token Management
- JWT structure (header, payload, signature)
- Access token vs ID token vs refresh token
- Token storage strategies (httpOnly cookies vs localStorage)
- Token refresh patterns
- Revocation and logout
Security Best Practices
- PKCE (Proof Key for Code Exchange)
- State parameter for CSRF protection
- Nonce for replay protection
- Secure token storage
- Short-lived access tokens
Identity Providers
- Auth0 integration
- Okta configuration
- Keycloak self-hosted
- Google/GitHub/Microsoft social login
- SAML to OIDC bridge
Dependencies
Works well with:
nextjs-app-router-expert - Full-stack auth implementation
api-architect - API authorization design
cloudflare-worker-dev - Edge authentication
site-reliability-engineer - Auth monitoring
Examples
Authorization Code Flow with PKCE (SPA)
function generatePKCE() {
const verifier = base64URLEncode(crypto.getRandomValues(new Uint8Array(32)));
const challenge = base64URLEncode(
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(verifier))
);
return { verifier, challenge };
}
function initiateLogin() {
const { verifier, challenge } = generatePKCE();
const state = crypto.randomUUID();
sessionStorage.setItem('pkce_verifier', verifier);
sessionStorage.setItem('oauth_state', state);
const params = new URLSearchParams({
response_type: 'code',
client_id: 'your-client-id',
redirect_uri: 'https://yourapp.com/callback',
scope: 'openid profile email',
state: state,
: challenge,
: ,
});
.. = ;
}
() {
(state !== .()) {
();
}
verifier = .();
response = (, {
: ,
: { : },
: ({
: ,
code,
: ,
: ,
: verifier,
}),
});
tokens = response.();
.();
.();
tokens;
}
Next.js API Route Token Handler
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
const state = searchParams.get('state');
const cookieStore = cookies();
const storedState = cookieStore.get('oauth_state')?.value;
if (state !== storedState) {
return NextResponse.redirect('/auth/error?reason=state_mismatch');
}
const tokenResponse = await fetch('https://auth.example.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new ({
: ,
: code!,
: process..!,
: process..!,
: process..!,
}),
});
tokens = tokenResponse.();
response = .();
response..(, tokens., {
: ,
: process.. === ,
: ,
: * * * ,
: ,
});
response..(, tokens., {
: ,
: process.. === ,
: ,
: tokens.,
: ,
});
response;
}
JWT Validation (Node.js)
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';
const client = jwksClient({
jwksUri: 'https://auth.example.com/.well-known/jwks.json',
cache: true,
rateLimit: true,
});
function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key?.getPublicKey();
callback(err, signingKey);
});
}
export async function validateToken(token: string): Promise<JWTPayload> {
return new Promise((resolve, reject) => {
jwt.verify(
token,
getKey,
{
algorithms: ['RS256'],
issuer: 'https://auth.example.com/',
audience: 'your-client-id',
},
(err, decoded) => {
(err) (err);
(decoded );
}
);
});
}
() {
token = req..()?.(, );
(!token) {
(, { : });
}
{
payload = (token);
{ : payload };
} (error) {
(, { : });
}
}
Token Refresh Pattern
let refreshPromise: Promise<string> | null = null;
async function getAccessToken(): Promise<string> {
const accessToken = localStorage.getItem('access_token');
const expiresAt = localStorage.getItem('token_expires_at');
if (accessToken && expiresAt && Date.now() < parseInt(expiresAt) - 60000) {
return accessToken;
}
if (refreshPromise) {
return refreshPromise;
}
refreshPromise = refreshAccessToken();
try {
return await refreshPromise;
} finally {
refreshPromise = null;
}
}
async function refreshAccessToken(): Promise<string> {
const response = await fetch('/api/auth/refresh', {
method: ,
: ,
});
(!response.) {
.. = ;
();
}
{ access_token, expires_in } = response.();
.(, access_token);
.(, (.() + expires_in * ));
access_token;
}
Social Login Setup (Auth0)
export const auth0Config = {
domain: process.env.AUTH0_DOMAIN!,
clientId: process.env.AUTH0_CLIENT_ID!,
clientSecret: process.env.AUTH0_CLIENT_SECRET!,
redirectUri: process.env.AUTH0_REDIRECT_URI!,
scope: 'openid profile email',
audience: process.env.AUTH0_AUDIENCE,
};
export function getLoginUrl(connection?: string) {
const params = new URLSearchParams({
response_type: 'code',
client_id: auth0Config.clientId,
redirect_uri: auth0Config.redirectUri,
scope: auth0Config.scope,
state: generateState(),
...(auth0Config.audience && { audience: auth0Config.audience }),
...(connection && { connection }),
});
return `https://${auth0Config.domain}/authorize?${params}`;
}
OIDC Discovery
interface OIDCConfig {
authorization_endpoint: string;
token_endpoint: string;
userinfo_endpoint: string;
jwks_uri: string;
issuer: string;
}
let oidcConfig: OIDCConfig | null = null;
export async function getOIDCConfig(issuer: string): Promise<OIDCConfig> {
if (oidcConfig) return oidcConfig;
const response = await fetch(`${issuer}/.well-known/openid-configuration`);
oidcConfig = await response.json();
return oidcConfig;
}
Best Practices
- Always use PKCE - Even for confidential clients, it adds security
- Validate state parameter - Prevents CSRF attacks
- Use httpOnly cookies - For refresh tokens, never localStorage
- Short-lived access tokens - 15 minutes is common, refresh as needed
- Validate tokens server-side - Don't trust client-side validation alone
- Use the nonce claim - Prevents replay attacks with ID tokens
- Implement proper logout - Revoke tokens and clear sessions
- Validate audience and issuer - Ensure tokens are for your app
- Rotate refresh tokens - Issue new refresh token on each use
Common Pitfalls
- Storing tokens in localStorage - Vulnerable to XSS attacks
- Not validating state - Opens CSRF vulnerability
- Implicit flow for SPAs - Deprecated, use Authorization Code + PKCE
- Long-lived access tokens - Increases risk if compromised
- Not validating JWT signature - Anyone can forge unsigned tokens
- Hardcoded client secrets - Use environment variables
- Missing token revocation - Users can't properly log out
- Not handling token expiry - Silent refresh failures cause bad UX