nitro-oidc-auth
v0.0.1
Published
OpenID Connect (OIDC) authentication for Nitro
Readme
Nitro OIDC Auth
🔐 Enterprise-grade OpenID Connect (OIDC) authentication module for Nitro applications.
Features
Core Features
- 🔐 OpenID Connect - Standard OIDC authentication flow (Authorization Code + PKCE)
- 🔒 PKCE Support - Proof Key for Code Exchange for enhanced security (public & confidential clients)
- 🔄 Auto Token Refresh - Automatic access token refresh before expiration
- 🍪 Session Management - Encrypted, production-ready session storage
- 🎯 Type Safe - Full TypeScript support with auto-generated types
- 🚀 Easy Integration - Simple setup with sensible defaults
- 🌐 Provider Agnostic - Works with any OIDC-compliant provider
🛡️ Enterprise Security Features (NEW!)
- ✅ ID Token Signature Verification - JWKS-based JWT validation
- ✅ Encrypted Session Storage - AES-256-GCM encryption with Unstorage drivers
- ✅ Cryptographically Secure Random - Web Crypto API for all random generation
- ✅ Nonce Parameter - ID token replay attack protection
- ✅ Refresh Token Rotation - OAuth 2.1 best practices (coming soon)
- ✅ Session Fixation Protection - Session ID regeneration after login (coming soon)
- ✅ Audit Logging - Security event logging for compliance
- ✅ Configurable Storage Drivers - Memory, Redis, Cloudflare KV, MongoDB, etc.
- ✅ Claims Validation - Issuer, audience, expiration, email verification
Installation
pnpm add nitro-oidc-authQuick Start
1. Configure Nitro
For Confidential Clients (Recommended for server-side):
// nitro.config.ts
import { defineNitroConfig } from 'nitropack/config'
export default defineNitroConfig({
modules: ['nitro-oidc-auth'],
oidc: {
issuer: 'https://your-provider.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/auth/callback',
},
})For Public Clients (PKCE enabled):
export default defineNitroConfig({
modules: ['nitro-oidc-auth'],
oidc: {
issuer: 'https://your-provider.com',
clientId: 'your-client-id',
// No clientSecret needed - PKCE is used for security
redirectUri: 'http://localhost:3000/auth/callback',
},
})2. Protect Your Routes
// routes/api/protected.ts
export default defineEventHandler(async (event) => {
const user = requireAuth(event)
return {
message: `Hello, ${user.name}!`,
user,
}
})3. Optional Authentication
// routes/api/optional.ts
export default defineEventHandler(async (event) => {
const user = useAuth(event)
if (user) {
return { message: `Welcome back, ${user.name}!` }
}
return { message: 'Hello, guest!' }
})Configuration
Basic Configuration
export default defineNitroConfig({
oidc: {
// Required
issuer: 'https://your-provider.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/auth/callback',
// Optional
postLogoutRedirectUri: 'http://localhost:3000',
scopes: ['openid', 'profile', 'email'],
basePath: '/auth',
},
})🔐 Production-Ready Configuration (Recommended)
export default defineNitroConfig({
oidc: {
// Provider Configuration
issuer: process.env.OIDC_ISSUER,
clientId: process.env.OIDC_CLIENT_ID,
clientSecret: process.env.OIDC_CLIENT_SECRET,
redirectUri: process.env.OIDC_REDIRECT_URI,
// Session Storage (Production)
sessionStorage: {
driver: 'redis',
options: {
base: 'oidc:',
url: process.env.REDIS_URL,
},
encryption: true,
encryptionSecret: process.env.SESSION_SECRET, // Strong secret!
},
// Security Features
security: {
nonce: true, // ID token replay protection
refreshTokenRotation: true, // OAuth 2.1 best practice
sessionFixationProtection: true, // Regenerate session ID after login
auditLog: true, // Log security events
// Rate Limiting (optional)
rateLimiting: {
enabled: true,
maxAttempts: 5,
windowMs: 15 * 60 * 1000, // 15 minutes
},
},
// Token Validation
tokenValidation: {
algorithms: ['RS256', 'ES256'], // Allowed algorithms
verifySignature: true, // JWKS verification
validateClaims: true, // Issuer, audience, exp
requireEmailVerified: false, // Strict email verification
},
// Token Options
usePKCE: true,
refreshThreshold: 300, // 5 minutes before expiry
},
})Session Storage Drivers
The module supports multiple storage backends via Unstorage:
Memory (Default - Development Only)
sessionStorage: {
driver: 'memory',
}Redis (Recommended for Production)
sessionStorage: {
driver: 'redis',
options: {
base: 'oidc:sessions:',
url: 'redis://localhost:6379',
// or
host: 'localhost',
port: 6379,
password: 'your-password',
},
encryption: true,
}Cloudflare KV
sessionStorage: {
driver: 'cloudflare-kv-binding',
options: {
binding: 'MY_KV_NAMESPACE',
},
encryption: true,
}MongoDB
sessionStorage: {
driver: 'mongodb',
options: {
connectionString: process.env.MONGODB_URL,
databaseName: 'auth',
collectionName: 'sessions',
},
encryption: true,
}Filesystem (Not recommended for production)
sessionStorage: {
driver: 'fs',
options: {
base: './.data/sessions',
},
encryption: true,
}Advanced Configuration
For custom provider endpoints (if not using standard discovery):
export default defineNitroConfig({
oidc: {
// ... basic config
providerConfig: {
authorizationEndpoint: 'https://provider.com/oauth/authorize',
tokenEndpoint: 'https://provider.com/oauth/token',
userinfoEndpoint: 'https://provider.com/oauth/userinfo',
endSessionEndpoint: 'https://provider.com/oauth/logout',
jwksUri: 'https://provider.com/.well-known/jwks.json',
},
},
})Authentication Flow
1. Login
Direct users to /auth/login to start authentication:
<a href="/auth/login">Login</a>Or with a return URL:
<a href="/auth/login?returnTo=/dashboard">Login</a>2. Callback
Users are redirected to /auth/callback after authentication. This is handled automatically.
3. Logout
Direct users to /auth/logout to end their session:
<a href="/auth/logout">Logout</a>4. User Info
Get current user info at /auth/me:
const response = await $fetch('/auth/me')
console.log(response.user)Helper Functions
Auto-imported helpers for easy authentication checks:
requireAuth(event)
Require authentication - throws 401 if not authenticated:
export default defineEventHandler(async (event) => {
const user = requireAuth(event)
// User is guaranteed to be authenticated here
return { user }
})useAuth(event)
Optional authentication - returns user or undefined:
export default defineEventHandler(async (event) => {
const user = useAuth(event)
if (user) {
return { authenticated: true, user }
}
return { authenticated: false }
})isAuthenticated(event)
Check if user is authenticated:
export default defineEventHandler(async (event) => {
if (isAuthenticated(event)) {
return { status: 'logged in' }
}
return { status: 'guest' }
})useAccessToken(event)
Get the access token for API calls:
export default defineEventHandler(async (event) => {
const token = useAccessToken(event)
// Use token to call external APIs
const data = await $fetch('https://api.example.com/data', {
headers: {
Authorization: `Bearer ${token}`,
},
})
return data
})User Object
The user object contains claims from the ID token:
interface OIDCUser {
sub: string // User ID
name?: string // Full name
email?: string // Email address
email_verified?: boolean // Email verification status
preferred_username?: string // Username
picture?: string // Profile picture URL
given_name?: string // First name
family_name?: string // Last name
locale?: string // User locale
[key: string]: any // Additional provider-specific claims
}Provider Examples
Keycloak
export default defineNitroConfig({
oidc: {
issuer: 'https://keycloak.example.com/realms/your-realm',
clientId: 'your-client',
clientSecret: 'your-secret',
redirectUri: 'http://localhost:3000/auth/callback',
},
})Auth0
export default defineNitroConfig({
oidc: {
issuer: 'https://your-tenant.auth0.com',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/auth/callback',
},
})Azure AD
export default defineNitroConfig({
oidc: {
issuer: 'https://login.microsoftonline.com/your-tenant-id/v2.0',
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/auth/callback',
scopes: ['openid', 'profile', 'email', 'offline_access'],
},
})export default defineNitroConfig({
oidc: {
issuer: 'https://accounts.google.com',
clientId: 'your-client-id.apps.googleusercontent.com',
clientSecret: 'your-client-secret',
redirectUri: 'http://localhost:3000/auth/callback',
},
})Routes
The module automatically registers these routes:
| Route | Method | Description |
|-------|--------|-------------|
| /auth/login | GET | Initiates OIDC authentication flow |
| /auth/callback | GET | Handles OIDC callback and creates session |
| /auth/logout | GET | Ends session and redirects to provider logout |
| /auth/me | GET | Returns current user info (requires authentication) |
Note: The base path /auth can be customized via the basePath configuration option.
Session Management
Sessions are stored in-memory and identified by secure HTTP-only cookies. The session includes:
- User information from ID token
- Access token for API calls
- Refresh token (if provided)
- Token expiration information
Automatic Token Refresh
The module automatically refreshes access tokens when they're close to expiring (configurable via refreshThreshold). This happens transparently in the middleware.
🛡️ Security Features
Implemented Security Best Practices
✅ PKCE (RFC 7636): Proof Key for Code Exchange enabled by default for all clients ✅ State Parameter: CSRF protection via cryptographically secure state validation ✅ Nonce Parameter: ID token replay attack protection ✅ ID Token Signature Verification: JWKS-based JWT validation with jose library ✅ Claims Validation: Issuer (iss), audience (aud), expiration (exp), email_verified ✅ Encrypted Sessions: AES-256-GCM encryption for session data ✅ Cryptographically Secure Random: Web Crypto API for all random generation (128-192 bits) ✅ Secure Cookies: HTTP-only, Secure (in production), SameSite=Lax/Strict ✅ Automatic Token Refresh: Seamless access token renewal before expiration ✅ Session Expiration: Configurable TTL with automatic cleanup ✅ Audit Logging: Security event logging for compliance
OAuth 2.1 & OIDC Compliance
- ✅ Authorization Code Flow with PKCE
- ✅ Refresh token support
- ✅ Provider discovery (
.well-known/openid-configuration) - ✅ Dynamic client registration support
- 🔄 Refresh token rotation (coming soon)
- 🔄 Session fixation protection (coming soon)
- 🔄 DPoP token binding (planned)
Security Headers & Cookies
All session cookies are set with:
HttpOnly: Prevents JavaScript access (XSS protection)Secure: HTTPS-only in productionSameSite: Lax or Strict (CSRF protection)- Short TTL with automatic cleanup
Audit Logging
When security.auditLog is enabled, the following events are logged:
// Login events
AUDIT: Login initiated
AUDIT: User logged in { userId, email, timestamp }
AUDIT: Login failed { error, timestamp }
// Token events
AUDIT: Token refreshed { userId, timestamp }
AUDIT: Token refresh failed { error, timestamp }
// Logout events
AUDIT: User logged out { userId, timestamp }TypeScript Support
Full TypeScript support with type definitions for all configuration options and user data:
import type { OIDCConfig, OIDCUser, OIDCSession } from 'nitro-oidc-auth'The module also extends H3's event context:
declare module 'h3' {
interface H3EventContext {
oidc?: {
session?: OIDCSession
user?: OIDCUser
}
}
}Environment Variables
You can also configure OIDC using environment variables in production:
NUXT_OIDC_ISSUER=https://your-provider.com
NUXT_OIDC_CLIENT_ID=your-client-id
NUXT_OIDC_CLIENT_SECRET=your-client-secret
NUXT_OIDC_REDIRECT_URI=https://your-app.com/auth/callbackDevelopment
# Install dependencies
pnpm install
# Build module
pnpm build
# Watch mode
pnpm dev
# Lint
pnpm lint
pnpm lint:fixLicense
MIT License
