Skip to content

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-tokens

OAuth with Google

bash
/template setup-auth oauth --provider google
bash
/template setup-auth magic-link --session-store kv

Full Authentication Suite

bash
/template setup-auth jwt --mfa --refresh-tokens --session-store d1

What 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 helpers

JWT 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

  1. CSRF Protection: State parameter for OAuth
  2. Rate Limiting: Prevent brute force attacks
  3. Token Blacklisting: Revoke compromised tokens
  4. Secure Headers: HSTS, CSP, etc.
  5. Password Hashing: Argon2 or bcrypt

Best Practices

  1. Use HTTPS: Always use secure connections
  2. Short Token Expiry: Keep access tokens short-lived
  3. Secure Storage: Never log sensitive data
  4. Input Validation: Validate all auth inputs
  5. 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();
  };
}

Built with ❤️ for the AI Coding community, by Praney Behl