authentication
Authentication patterns including JWT, sessions, OAuth 2.0, OIDC, passkeys, RBAC, and multi-factor authentication
Best use case
authentication is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Authentication patterns including JWT, sessions, OAuth 2.0, OIDC, passkeys, RBAC, and multi-factor authentication
Teams using authentication should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/authentication/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How authentication Compares
| Feature / Agent | authentication | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Authentication patterns including JWT, sessions, OAuth 2.0, OIDC, passkeys, RBAC, and multi-factor authentication
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# Authentication Specialist
## Purpose
Design and implement secure authentication systems including token-based auth (JWT), server-side sessions, OAuth 2.0/OIDC flows, passkeys (WebAuthn), role-based access control (RBAC), and multi-factor authentication (MFA).
## Key Patterns
### Authentication Strategy Comparison
| Strategy | Best For | Pros | Cons |
|----------|----------|------|------|
| **Sessions** | Traditional web apps | Simple, revocable, secure | Requires session store |
| **JWT** | APIs, microservices | Stateless, scalable | Hard to revoke, size |
| **OAuth 2.0** | Third-party login | Delegated auth, standard | Complex flows |
| **Passkeys** | Passwordless | Phishing-resistant, UX | Browser support varies |
### JWT Token Flow (Access + Refresh)
```typescript
import { SignJWT, jwtVerify } from "jose";
const ACCESS_SECRET = new TextEncoder().encode(process.env.JWT_ACCESS_SECRET);
const REFRESH_SECRET = new TextEncoder().encode(process.env.JWT_REFRESH_SECRET);
// Access token: short-lived, contains claims
async function createAccessToken(user: { id: string; role: string }) {
return new SignJWT({ sub: user.id, role: user.role })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("15m")
.setIssuer("my-app")
.sign(ACCESS_SECRET);
}
// Refresh token: long-lived, stored securely
async function createRefreshToken(userId: string) {
return new SignJWT({ sub: userId })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("7d")
.setJti(crypto.randomUUID()) // Unique ID for revocation
.setIssuer("my-app")
.sign(REFRESH_SECRET);
}
// Verify token
async function verifyAccessToken(token: string) {
try {
const { payload } = await jwtVerify(token, ACCESS_SECRET, { issuer: "my-app" });
return payload;
} catch {
return null;
}
}
// Token refresh endpoint
async function refreshTokens(refreshToken: string) {
const payload = await jwtVerify(refreshToken, REFRESH_SECRET, { issuer: "my-app" });
// Check if refresh token is revoked (store JTI in DB/Redis)
const isRevoked = await checkRevoked(payload.payload.jti as string);
if (isRevoked) throw new Error("Token revoked");
// Rotate: revoke old, issue new pair
await revokeToken(payload.payload.jti as string);
const user = await getUser(payload.payload.sub as string);
return {
accessToken: await createAccessToken(user),
refreshToken: await createRefreshToken(user.id),
};
}
```
### Session-Based Auth (with Cookies)
```typescript
import { cookies } from "next/headers";
import { SignJWT, jwtVerify } from "jose";
const SESSION_SECRET = new TextEncoder().encode(process.env.SESSION_SECRET);
export async function createSession(userId: string) {
const token = await new SignJWT({ userId })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("24h")
.sign(SESSION_SECRET);
const cookieStore = await cookies();
cookieStore.set("session", token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 86400,
path: "/",
});
}
export async function getSession() {
const cookieStore = await cookies();
const token = cookieStore.get("session")?.value;
if (!token) return null;
try {
const { payload } = await jwtVerify(token, SESSION_SECRET);
return { userId: payload.userId as string };
} catch {
return null;
}
}
export async function deleteSession() {
const cookieStore = await cookies();
cookieStore.delete("session");
}
```
### OAuth 2.0 Authorization Code Flow
```typescript
// Step 1: Redirect to provider
function getAuthorizationUrl(provider: "google" | "github") {
const configs = {
google: {
authUrl: "https://accounts.google.com/o/oauth2/v2/auth",
clientId: process.env.GOOGLE_CLIENT_ID,
scopes: "openid email profile",
},
github: {
authUrl: "https://github.com/login/oauth/authorize",
clientId: process.env.GITHUB_CLIENT_ID,
scopes: "read:user user:email",
},
};
const config = configs[provider];
const state = crypto.randomUUID(); // Store in session for CSRF protection
const params = new URLSearchParams({
client_id: config.clientId!,
redirect_uri: `${process.env.APP_URL}/api/auth/callback/${provider}`,
response_type: "code",
scope: config.scopes,
state,
});
return `${config.authUrl}?${params}`;
}
// Step 2: Handle callback
async function handleCallback(provider: string, code: string, state: string) {
// Verify state matches session
const tokenUrl = provider === "google"
? "https://oauth2.googleapis.com/token"
: "https://github.com/login/oauth/access_token";
// Exchange code for tokens
const tokenResponse = await fetch(tokenUrl, {
method: "POST",
headers: { "Content-Type": "application/json", Accept: "application/json" },
body: JSON.stringify({
client_id: process.env[`${provider.toUpperCase()}_CLIENT_ID`],
client_secret: process.env[`${provider.toUpperCase()}_CLIENT_SECRET`],
code,
redirect_uri: `${process.env.APP_URL}/api/auth/callback/${provider}`,
grant_type: "authorization_code",
}),
});
const tokens = await tokenResponse.json();
// Fetch user profile, create/update user, create session
return tokens;
}
```
### RBAC (Role-Based Access Control)
```typescript
type Role = "admin" | "editor" | "viewer";
type Permission = "read" | "write" | "delete" | "manage_users";
const rolePermissions: Record<Role, Permission[]> = {
admin: ["read", "write", "delete", "manage_users"],
editor: ["read", "write"],
viewer: ["read"],
};
function hasPermission(userRole: Role, permission: Permission): boolean {
return rolePermissions[userRole]?.includes(permission) ?? false;
}
// Middleware
function requirePermission(permission: Permission) {
return async (req: Request) => {
const session = await getSession();
if (!session) return new Response("Unauthorized", { status: 401 });
const user = await getUser(session.userId);
if (!hasPermission(user.role as Role, permission)) {
return new Response("Forbidden", { status: 403 });
}
};
}
```
## Best Practices
### Password Handling
- Hash with Argon2id (preferred) or bcrypt (min cost 12)
- Never store plaintext passwords
- Enforce minimum 8 characters, check against breached password lists
- Use constant-time comparison for hash verification
### Token Security
- Access tokens: 15 minutes max, stored in memory only
- Refresh tokens: 7-30 days, httpOnly cookie or secure storage
- Rotate refresh tokens on every use (detect reuse = compromise)
- Include `iss`, `exp`, `sub`, `jti` claims in JWTs
### Cookie Security
- `httpOnly`: prevent JavaScript access
- `secure`: HTTPS only
- `sameSite: lax`: CSRF protection for most cases
- `path: /`: or scope to specific routes
### OAuth 2.0
- Always use the Authorization Code flow (not Implicit)
- Validate `state` parameter to prevent CSRF
- Use PKCE for public clients (SPAs, mobile)
- Store provider tokens encrypted, not in plain text
### MFA
- Support TOTP (Google Authenticator) as baseline
- Offer WebAuthn/passkeys as phishing-resistant option
- Provide recovery codes (10 single-use codes)
- Rate limit MFA verification attempts
## Common Pitfalls
| Pitfall | Fix |
|---------|-----|
| Storing JWT in localStorage | Use httpOnly cookies for refresh tokens |
| No token rotation | Rotate refresh tokens on each use |
| Missing CSRF protection | Use `sameSite` cookies + state parameter |
| Password in URL parameters | Always send credentials in POST body |
| No rate limiting on login | Rate limit by IP and username |
| Hardcoded secrets | Use environment variables, rotate regularly |
| Missing logout flow | Clear session, revoke tokens, clear cookies |
## Examples
### Password Hashing with Argon2
```typescript
import { hash, verify } from "@node-rs/argon2";
async function hashPassword(password: string): Promise<string> {
return hash(password, {
memoryCost: 19456,
timeCost: 2,
outputLen: 32,
parallelism: 1,
});
}
async function verifyPassword(hash: string, password: string): Promise<boolean> {
return verify(hash, password);
}
```
### Next.js Auth Middleware
```typescript
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
import { jwtVerify } from "jose";
const protectedPaths = ["/dashboard", "/settings", "/api/user"];
const authPaths = ["/login", "/signup"];
const SECRET = new TextEncoder().encode(process.env.SESSION_SECRET);
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const session = request.cookies.get("session")?.value;
const isProtected = protectedPaths.some((p) => pathname.startsWith(p));
const isAuthPage = authPaths.some((p) => pathname.startsWith(p));
let isValid = false;
if (session) {
try {
await jwtVerify(session, SECRET);
isValid = true;
} catch {}
}
if (isProtected && !isValid) {
return NextResponse.redirect(new URL(`/login?callbackUrl=${pathname}`, request.url));
}
if (isAuthPage && isValid) {
return NextResponse.redirect(new URL("/dashboard", request.url));
}
return NextResponse.next();
}
```Related Skills
ultrathink
UltraThink Workflow OS — 4-layer skill mesh with persistent memory and privacy hooks for complex engineering tasks. Routes prompts through intent detection to activate the right domain skills automatically.
ultrathink_review
Multi-pass code review powered by UltraThink's quality gate — checks correctness, security (OWASP), performance, readability, and project conventions in a single structured pass.
ultrathink_memory
Persistent memory system for UltraThink — search, save, and recall project context, decisions, and patterns across sessions using Postgres-backed fuzzy search with synonym expansion.
ui-design
Comprehensive UI design system: 230+ font pairings, 48 themes, 65 design systems, 23 design languages, 30 UX laws, 14 color systems, Swiss grid, Gestalt principles, Pencil.dev workflow. Inherits ui-ux-pro-max (99 UX rules) + impeccable-frontend-design (anti-AI-slop). Triggers on any design, UI, layout, typography, color, theme, or styling task.
Zod
> TypeScript-first schema validation with static type inference.
webinar-registration-page
Build a webinar or live event registration page as a self-contained HTML file with countdown timer, speaker bio, agenda, and registration form. Triggers on: "build a webinar registration page", "create a webinar sign-up page", "event registration landing page", "live training registration page", "workshop sign-up page", "create a webinar page", "build an event page", "free webinar landing page", "live demo registration page", "online event page", "create a registration page for my webinar", "build a training event page".
webhooks
Webhook design patterns — delivery, retry with exponential backoff, HMAC signature verification, payload validation, idempotency keys
web-workers
Offload heavy computation from the main thread using Web Workers, SharedWorkers, and Comlink — structured messaging, transferable objects, and off-main-thread architecture patterns
web-vitals
Core Web Vitals monitoring (LCP, FID, CLS, INP, TTFB), measurement with web-vitals library, reporting to analytics, and optimization strategies for Next.js
web-components
Native Web Components, custom elements API, Shadow DOM, HTML templates, slots, lifecycle callbacks, and framework-agnostic design patterns
wasm
WebAssembly integration — Rust to WASM with wasm-pack/wasm-bindgen, WASI, browser usage, server-side WASM, and performance considerations
vue
Vue 3 Composition API, Nuxt patterns, reactivity system, component architecture, and production development practices