Quick Start
Workers Auth gives you authentication, authorization, and user management that runs entirely on Cloudflare’s edge. This guide walks you through a complete setup: magic link login, GitHub OAuth, role-based access control, and a full admin API.
-
Install the packages
Workers Auth is built on Hono. Install both:
Terminal window npm install workers-auth hono -
Create your infrastructure
Workers Auth stores users and sessions in D1, with KV as a fast session cache. Create both from the command line:
Terminal window wrangler d1 create authwrangler kv namespace create SESSIONSThen wire the returned IDs into your
wrangler.toml:name = "my-app"main = "src/index.ts"compatibility_date = "2024-12-01"[[d1_databases]]binding = "DB"database_name = "auth"database_id = "<your-database-id>"[[kv_namespaces]]binding = "SESSIONS"id = "<your-kv-id>" -
Set your secrets
Workers Auth needs API keys for email delivery and (optionally) OAuth. Store them as encrypted secrets — never in source control:
Terminal window wrangler secret put RESEND_API_KEYwrangler secret put GITHUB_CLIENT_IDwrangler secret put GITHUB_CLIENT_SECRET -
Build your app
This is the complete Worker. It configures both authentication strategies, sets up RBAC, and mounts the auth handler alongside the admin API:
import { Hono } from 'hono';import { WorkersAuth } from 'workers-auth';import { MagicLinkStrategy } from 'workers-auth/authn/magic-link';import { GitHubStrategy } from 'workers-auth/authn/github';import { RBACPolicy } from 'workers-auth/authz/rbac';import { ResendProvider } from 'workers-auth/providers/resend';import { D1Adapter } from 'workers-auth/adapters/d1';import { KVAdapter } from 'workers-auth/adapters/kv';import { defaultTemplate } from 'workers-auth/templates/default';type Env = {DB: D1Database;SESSIONS: KVNamespace;RESEND_API_KEY: string;GITHUB_CLIENT_ID: string;GITHUB_CLIENT_SECRET: string;};const app = new Hono<{ Bindings: Env }>();const auth = WorkersAuth({database: (binding) => D1Adapter(binding),cache: (binding) => KVAdapter(binding),authn: {strategies: [MagicLinkStrategy({provider: ResendProvider({apiKey: process.env.RESEND_API_KEY!,from: 'auth@yourdomain.com',}),template: defaultTemplate,}),GitHubStrategy({clientId: process.env.GITHUB_CLIENT_ID!,clientSecret: process.env.GITHUB_CLIENT_SECRET!,}),],session: { secret: 'your-secret-key-here' },},authz: RBACPolicy({roles: { admin: ['*'], user: ['read'] },defaultRole: 'user',}),redirectUrl: '/dashboard',});// Auth routes — login, logout, session managementapp.route('/auth', auth.handler);// Admin API — user management (protect with auth + admin role)app.use('/auth/admin/*', auth.authenticate);app.use('/auth/admin/*', auth.authorize('role', 'admin'));app.route('/auth/admin', auth.admin);// Protect your API routes — requires a valid sessionapp.use('/api/*', auth.authenticate);// Your application routesapp.get('/api/me', (c) => c.json(c.get('user')));export default app; -
Get a Resend API key
Resend handles magic link email delivery. Sign up, verify your sending domain, and create an API key. You already stored it as a secret in step 3.
-
Create a GitHub OAuth App (optional)
If you included the GitHub strategy:
- Open GitHub Developer Settings and click New OAuth App
- Set the callback URL to
https://your-app.workers.dev/auth/github/callback - Copy the Client ID and Client Secret — you stored these as secrets in step 3
-
Deploy
Terminal window wrangler deployD1 tables are created automatically on the first request. No migrations, no setup scripts.
What you just built
Section titled “What you just built”That single file gave you a production-ready auth system. Here is every route Workers Auth registered:
Authentication routes
Section titled “Authentication routes”Mounted via app.route('/auth', auth.handler):
| Route | Method | Description |
|---|---|---|
/auth/magic-link/send | POST | Send a magic link email |
/auth/magic-link/verify | GET | Verify the token and create a session |
/auth/github | GET | Start the GitHub OAuth flow |
/auth/github/callback | GET | Handle the GitHub OAuth callback |
/auth/session | GET | Return the current session and user |
/auth/logout | POST | Destroy the active session |
Admin API routes
Section titled “Admin API routes”Mounted via app.route('/auth/admin', auth.admin), protected behind authenticate and authorize('role', 'admin'):
| Route | Method | Description |
|---|---|---|
/auth/admin/users | GET | List users with pagination |
/auth/admin/users/:id | GET | Get a single user with roles |
/auth/admin/users/:id/ban | POST | Ban a user and revoke all sessions |
/auth/admin/users/:id/unban | POST | Unban a user |
/auth/admin/users/:id/roles | POST | Assign a role to a user |
/auth/admin/users/:id/roles/:role | DELETE | Remove a role from a user |
Application routes
Section titled “Application routes”Defined in your app code, protected by auth.authenticate:
| Route | Method | Description |
|---|---|---|
/api/me | GET | Return the authenticated user |
Next steps
Section titled “Next steps”- Magic Links — customize email templates and link expiry
- GitHub OAuth — configure scopes and callback handling
- RBAC — define roles and permissions for your app
- D1 Adapter — database schema and custom adapters
- KV Adapter — session caching and custom cache adapters
- API Reference — full list of exports and types