| name | oauth-patterns |
| description | OIDC flows, PKCE implementation, token refresh strategies, social login integration, and secure session management. |
OAuth Patterns
Secure authentication and authorization patterns with OAuth 2.0 and OpenID Connect.
Authorization Code Flow with PKCE
import crypto from 'crypto'
function generatePKCE(): { verifier: string; challenge: string } {
const verifier = crypto.randomBytes(32).toString('base64url')
const challenge = crypto
.createHash('sha256')
.update(verifier)
.digest('base64url')
return { verifier, challenge }
}
function getAuthorizationUrl(config: OAuthConfig): { url: string; state: string; pkce: PKCE } {
const state = crypto.randomBytes(16).toString('hex')
const pkce = generatePKCE()
const params = new URLSearchParams({
response_type: 'code',
client_id: config.clientId,
redirect_uri: config.redirectUri,
scope: 'openid profile email',
state,
code_challenge: pkce.challenge,
code_challenge_method: 'S256',
prompt: 'consent',
nonce: crypto.randomUUID(),
})
return {
url: `${config.authorizationEndpoint}?${params}`,
state,
pkce,
}
}
async function exchangeCodeForTokens(
code: string,
verifier: string,
config: OAuthConfig
): Promise<TokenSet> {
const response = await fetch(config.tokenEndpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'authorization_code',
code,
redirect_uri: config.redirectUri,
client_id: config.clientId,
code_verifier: verifier,
}),
})
if (!response.ok) {
const error = await response.json()
throw new Error(`Token exchange failed: ${error.error_description}`)
}
return response.json() as Promise<TokenSet>
}
Token Refresh Strategy
interface TokenSet {
access_token: string
refresh_token: string
id_token: string
expires_in: number
token_type: 'Bearer'
}
class TokenManager {
private refreshTimer: NodeJS.Timeout | null = null
async setTokens(tokens: TokenSet): Promise<void> {
await secureStore.set('access_token', tokens.access_token)
await secureStore.set('refresh_token', tokens.refresh_token)
const refreshIn = tokens.expires_in * 0.75 * 1000
this.scheduleRefresh(refreshIn)
}
private scheduleRefresh(delayMs: number): void {
if (this.) (.)
. = ( .(), delayMs)
}
(): <> {
refreshToken = secureStore.()
(!refreshToken) {
.()
}
{
response = (config., {
: ,
: { : },
: ({
: ,
: refreshToken,
: config.,
}),
})
(!response.) ()
tokens = response.()
.(tokens)
} (err) {
.()
.()
}
}
(): <> {
(.) (.)
secureStore.()
secureStore.()
}
}
Social Login Integration
const OAUTH_PROVIDERS: Record<string, OAuthConfig> = {
google: {
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
authorizationEndpoint: 'https://accounts.google.com/o/oauth2/v2/auth',
tokenEndpoint: 'https://oauth2.googleapis.com/token',
userInfoEndpoint: 'https://www.googleapis.com/oauth2/v3/userinfo',
scopes: ['openid', 'profile', 'email'],
},
github: {
clientId: process.env.GITHUB_CLIENT_ID!,
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
authorizationEndpoint: 'https://github.com/login/oauth/authorize',
tokenEndpoint: 'https://github.com/login/oauth/access_token',
userInfoEndpoint: 'https://api.github.com/user',
scopes: ['user:email'],
},
}
async function handleOAuthCallback(
provider: string,
code: string,
state:
): <{ : ; : }> {
storedState = sessionStore.()
(!storedState) ()
config = [provider]
(!config) ()
tokens = (code, storedState., config)
profile = (config., tokens.)
user = db..({
: {
: {
: { provider, : profile. }
}
}
})
(!user) {
user = db..({
: {
: profile.,
: profile.,
: {
: {
provider,
: profile.,
: profile.,
}
}
}
})
}
session = (user.)
{ user, session }
}
Session Management
import { SignJWT, jwtVerify } from 'jose'
const SESSION_SECRET = new TextEncoder().encode(process.env.SESSION_SECRET!)
async function createSession(userId: string): Promise<string> {
const sessionId = crypto.randomUUID()
await db.session.create({
data: {
id: sessionId,
userId,
expiresAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
createdAt: new Date(),
}
})
const token = await new SignJWT({ sub: userId, sid: sessionId })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime()
.()
token
}
(): {
res.(, token, {
: ,
: ,
: ,
: * * * ,
: ,
: ,
})
}
() {
token = req..
(!token) res.().({ : })
{
{ payload } = (token, )
session = db..({ : { : payload. } })
(!session || session. < ()) {
res.().({ : })
}
req. = { : payload. , : session. }
()
} {
res.().({ : })
}
}
Checklist
Anti-Patterns
- Storing tokens in localStorage (XSS exposes all tokens)
- Implicit grant flow (deprecated, tokens in URL fragment)
- Not validating state parameter: vulnerable to CSRF
- Long-lived access tokens without refresh mechanism
- Trusting JWT without server-side session validation (no revocation)
- Hardcoding client secrets in frontend code (use backend proxy)