setup-auth
Implement authentication with JWT, OAuth, or other providers.
Overview
The setup-auth command creates a complete authentication system for your Cloudflare Workers application, supporting JWT tokens, OAuth providers, and session management with Workers KV.
Usage
bash
/template setup-auth <auth-type> [options]Parameters
<auth-type>- Type of authentication:jwt,oauth,magic-link,passwordless
Options
--provider- OAuth provider (google, github, discord)--session-store- Session storage type (kv, d1)--mfa- Enable multi-factor authentication--refresh-tokens- Enable refresh token rotation
Examples
JWT Authentication
bash
/template setup-auth jwt --refresh-tokensOAuth with Google
bash
/template setup-auth oauth --provider googleMagic Link Authentication
bash
/template setup-auth magic-link --session-store kvFull Authentication Suite
bash
/template setup-auth jwt --mfa --refresh-tokens --session-store d1What It Creates
Authentication Structure
src/
├── auth/
│ ├── providers/
│ │ ├── jwt.ts # JWT implementation
│ │ ├── oauth.ts # OAuth providers
│ │ └── magic-link.ts # Magic link auth
│ ├── middleware.ts # Auth middleware
│ ├── session.ts # Session management
│ ├── tokens.ts # Token utilities
│ └── utils.ts # Auth helpersJWT Implementation
typescript
// src/auth/providers/jwt.ts
import { SignJWT, jwtVerify } from '@tsndr/cloudflare-worker-jwt';
export async function createToken(userId: string, env: Env) {
const token = await new SignJWT({
sub: userId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour
})
.setProtectedHeader({ alg: 'HS256' })
.sign(env.JWT_SECRET);
return token;
}
export async function verifyToken(token: string, env: Env) {
try {
const { payload } = await jwtVerify(token, env.JWT_SECRET);
return payload;
} catch {
return null;
}
}OAuth Provider Setup
typescript
// src/auth/providers/oauth.ts
export class GoogleOAuth {
constructor(private env: Env) {}
getAuthUrl(state: string): string {
const params = new URLSearchParams({
client_id: this.env.GOOGLE_CLIENT_ID,
redirect_uri: `${this.env.APP_URL}/auth/google/callback`,
response_type: 'code',
scope: 'openid email profile',
state,
});
return `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
}
async exchangeCode(code: string) {
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
code,
client_id: this.env.GOOGLE_CLIENT_ID,
client_secret: this.env.GOOGLE_CLIENT_SECRET,
redirect_uri: `${this.env.APP_URL}/auth/google/callback`,
grant_type: 'authorization_code',
}),
});
return response.json();
}
}Auth Middleware
typescript
// src/auth/middleware.ts
export function requireAuth(): MiddlewareHandler {
return async (c, next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return c.json({ error: 'Unauthorized' }, 401);
}
const payload = await verifyToken(token, c.env);
if (!payload) {
return c.json({ error: 'Invalid token' }, 401);
}
c.set('userId', payload.sub);
await next();
};
}Session Management
KV Session Store
typescript
// src/auth/session.ts
export class KVSessionStore {
constructor(private kv: KVNamespace) {}
async create(userId: string, data?: any) {
const sessionId = crypto.randomUUID();
await this.kv.put(
`session:${sessionId}`,
JSON.stringify({ userId, data }),
{ expirationTtl: 86400 } // 24 hours
);
return sessionId;
}
async get(sessionId: string) {
const data = await this.kv.get(`session:${sessionId}`, 'json');
return data;
}
async destroy(sessionId: string) {
await this.kv.delete(`session:${sessionId}`);
}
}Refresh Token Rotation
typescript
// src/auth/tokens.ts
export async function rotateRefreshToken(oldToken: string, env: Env) {
const payload = await verifyRefreshToken(oldToken, env);
if (!payload) return null;
// Invalidate old token
await env.KV.put(`blacklist:${oldToken}`, '1', {
expirationTtl: 604800 // 7 days
});
// Create new tokens
const accessToken = await createToken(payload.sub, env);
const refreshToken = await createRefreshToken(payload.sub, env);
return { accessToken, refreshToken };
}Multi-Factor Authentication
TOTP Setup
typescript
// src/auth/mfa/totp.ts
import { TOTP } from '@epic-web/totp';
export async function setupTOTP(userId: string) {
const secret = generateSecret();
const totp = new TOTP({ secret });
return {
secret,
qrCode: totp.qrcode({
issuer: 'MyApp',
label: userId,
}),
};
}
export async function verifyTOTP(secret: string, code: string) {
const totp = new TOTP({ secret });
return totp.validate({ code });
}API Endpoints
Login Endpoint
typescript
app.post('/auth/login', async (c) => {
const { email, password } = await c.req.json();
const user = await getUserByEmail(email, c.env.DB);
if (!user || !await verifyPassword(password, user.password)) {
return c.json({ error: 'Invalid credentials' }, 401);
}
const token = await createToken(user.id, c.env);
return c.json({ token });
});OAuth Callback
typescript
app.get('/auth/:provider/callback', async (c) => {
const { provider } = c.req.param();
const { code, state } = c.req.query();
// Verify state to prevent CSRF
if (!await verifyState(state, c.env)) {
return c.json({ error: 'Invalid state' }, 400);
}
const oauth = getOAuthProvider(provider, c.env);
const tokens = await oauth.exchangeCode(code);
const user = await oauth.getUser(tokens.access_token);
// Create or update user
const dbUser = await findOrCreateUser(user, c.env.DB);
const sessionId = await createSession(dbUser.id, c.env);
return c.redirect(`/?session=${sessionId}`);
});Security Features
- CSRF Protection: State parameter for OAuth
- Rate Limiting: Prevent brute force attacks
- Token Blacklisting: Revoke compromised tokens
- Secure Headers: HSTS, CSP, etc.
- Password Hashing: Argon2 or bcrypt
Best Practices
- Use HTTPS: Always use secure connections
- Short Token Expiry: Keep access tokens short-lived
- Secure Storage: Never log sensitive data
- Input Validation: Validate all auth inputs
- Audit Logging: Log authentication events
Common Patterns
Protected Routes
typescript
const protected = new Hono();
protected.use('*', requireAuth());
protected.get('/profile', async (c) => {
const userId = c.get('userId');
const user = await getUser(userId, c.env.DB);
return c.json(user);
});Role-Based Access
typescript
export function requireRole(role: string): MiddlewareHandler {
return async (c, next) => {
const userId = c.get('userId');
const user = await getUser(userId, c.env.DB);
if (!user.roles.includes(role)) {
return c.json({ error: 'Forbidden' }, 403);
}
await next();
};
}Related Commands
setup-database- Store user dataadd-middleware- Add auth middlewaresetup-analytics- Track auth events
