Authentication

Protect routes with authentication.

Basic Auth

HTTP Basic Authentication:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const auth = request.headers.get('Authorization');

    if (!auth || !auth.startsWith('Basic ')) {
      return new Response('Unauthorized', {
        status: 401,
        headers: { 'WWW-Authenticate': 'Basic realm="Protected"' }
      });
    }

    const credentials = atob(auth.slice(6));
    const [username, password] = credentials.split(':');

    if (username !== env.USERNAME || password !== env.PASSWORD) {
      return new Response('Invalid credentials', { status: 403 });
    }

    return new Response('Welcome, ' + username);
  }
};

Set USERNAME and PASSWORD as secrets in your environment.

Bearer Token

API key or token authentication:

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const auth = request.headers.get('Authorization');

    if (!auth || !auth.startsWith('Bearer ')) {
      return Response.json({ error: 'Missing token' }, { status: 401 });
    }

    const token = auth.slice(7);

    if (token !== env.API_KEY) {
      return Response.json({ error: 'Invalid token' }, { status: 403 });
    }

    return Response.json({ data: 'Protected resource' });
  }
};

JWT Validation

Validate JWT tokens using HMAC:

async function verifyJWT(token: string, secret: string): Promise<object | null> {
  const [headerB64, payloadB64, signatureB64] = token.split('.');

  if (!headerB64 || !payloadB64 || !signatureB64) {
    return null;
  }

  // Verify signature
  const encoder = new TextEncoder();
  const data = encoder.encode(`${headerB64}.${payloadB64}`);
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['verify']
  );

  const signature = Uint8Array.from(atob(signatureB64.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
  const valid = await crypto.subtle.verify('HMAC', key, signature, data);

  if (!valid) {
    return null;
  }

  // Decode payload
  const payload = JSON.parse(atob(payloadB64));

  // Check expiration
  if (payload.exp && payload.exp < Date.now() / 1000) {
    return null;
  }

  return payload;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const auth = request.headers.get('Authorization');

    if (!auth || !auth.startsWith('Bearer ')) {
      return Response.json({ error: 'Missing token' }, { status: 401 });
    }

    const token = auth.slice(7);
    const payload = await verifyJWT(token, env.JWT_SECRET);

    if (!payload) {
      return Response.json({ error: 'Invalid token' }, { status: 403 });
    }

    return Response.json({ user: payload });
  }
};

Protected Routes

Middleware pattern for multiple protected routes:

type Handler = (request: Request, env: Env, user: object) => Promise<Response>;

async function withAuth(
  request: Request,
  env: Env,
  handler: Handler
): Promise<Response> {
  const auth = request.headers.get('Authorization');

  if (!auth || !auth.startsWith('Bearer ')) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 });
  }

  const token = auth.slice(7);

  // Validate token (simplified - use JWT in production)
  const user = await env.KV.get(`session:${token}`, 'json');

  if (!user) {
    return Response.json({ error: 'Invalid session' }, { status: 403 });
  }

  return handler(request, env, user);
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const { pathname } = new URL(request.url);

    // Public routes
    if (pathname === '/login') {
      return handleLogin(request, env);
    }

    // Protected routes
    if (pathname === '/profile') {
      return withAuth(request, env, handleProfile);
    }

    if (pathname === '/settings') {
      return withAuth(request, env, handleSettings);
    }

    return new Response('Not Found', { status: 404 });
  }
};

async function handleLogin(request: Request, env: Env): Promise<Response> {
  // Login logic...
  return Response.json({ token: 'generated-session-token' });
}

async function handleProfile(request: Request, env: Env, user: object): Promise<Response> {
  return Response.json({ user });
}

async function handleSettings(request: Request, env: Env, user: object): Promise<Response> {
  return Response.json({ settings: { theme: 'dark' } });
}