Files
gh-hirefrank-hirefrank-mark…/commands/es-auth-setup.md
2025-11-29 18:45:50 +08:00

7.3 KiB

description
description
Interactive authentication setup wizard. Configures better-auth, OAuth providers, and generates handlers for Cloudflare Workers.

Authentication Setup Command

<command_purpose> Guide developers through authentication stack configuration with automated code generation, database migrations, and MCP-driven provider setup. </command_purpose>

Introduction

Senior Security Engineer with expertise in authentication, better-auth, and Cloudflare Workers security

This command will:

  • Detect framework (Tanstack Start vs standalone Worker)
  • Configure better-auth for all authentication needs
  • Query better-auth MCP for OAuth provider requirements
  • Generate login/register/logout handlers with React Server Functions
  • Create D1 database schema for users/sessions
  • Configure session security (HTTPS cookies, CSRF)
  • Generate environment variables template

Prerequisites

- Cloudflare Workers project (Tanstack Start or Hono) - D1 database configured (or will create) - For OAuth: Provider credentials (Google, GitHub, etc.)

Main Tasks

1. Detect Framework & Auth Requirements

Ask User:

🔐 Authentication Setup Wizard

1. What framework are you using?
   a) Tanstack Start
   b) Standalone Worker (Hono/plain TS)

2. What authentication methods do you need?
   a) Email/Password only
   b) OAuth providers (Google, GitHub, etc.)
   c) Passkeys
   d) Magic Links
   e) Multiple (OAuth + Email/Password)

Decision Logic:

If Tanstack Start:
  → better-auth with React Server Functions

If Standalone Worker (Hono):
  → better-auth with Hono middleware

2. Install Dependencies

For Tanstack Start:

pnpm add better-auth @node-rs/argon2

For Standalone Worker (Hono):

pnpm add better-auth hono @node-rs/argon2

3. Generate Configuration Files

Tanstack Start + better-auth

Generate: app/auth.server.ts

import { betterAuth } from 'better-auth';

export const auth = betterAuth({
  database: {
    type: 'd1',
    database: process.env.DB,
  },

  emailAndPassword: {
    enabled: true,
    requireEmailVerification: true,
  },

  session: {
    cookieName: 'session',
    maxAge: 60 * 60 * 24 * 7, // 7 days
    cookieCache: {
      enabled: true,
      maxAge: 5 * 60 * 1000, // 5 minutes
    },
  },
});

Generate: server/api/auth/login.post.ts

import { hash, verify } from '@node-rs/argon2';

export default defineEventHandler(async (event) => {
  const { email, password } = await readBody(event);

  const user = await event.context.cloudflare.env.DB.prepare(
    'SELECT id, email, password_hash FROM users WHERE email = ?'
  ).bind(email).first();

  if (!user || !await verify(user.password_hash, password)) {
    throw createError({ statusCode: 401, message: 'Invalid credentials' });
  }

  await setUserSession(event, {
    user: { id: user.id, email: user.email },
    loggedInAt: new Date().toISOString(),
  });

  return { success: true };
});

Query MCP for OAuth Setup:

const googleSetup = await mcp.betterAuth.getProviderSetup('google');
const githubSetup = await mcp.betterAuth.getProviderSetup('github');

Generate: server/utils/auth.ts

import { betterAuth } from 'better-auth';

export const auth = betterAuth({
  database: {
    type: 'd1',
    database: process.env.DB,
  },

  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    },
    github: {
      clientId: process.env.GITHUB_CLIENT_ID!,
      clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    },
  },
});

Generate: server/api/auth/[...].ts (OAuth handler)

export default defineEventHandler(async (event) => {
  const response = await auth.handler(event.node.req, event.node.res);

  // 
  if (event.node.req.url?.includes('/callback')) {
    const session = await auth.api.getSession({ headers: event.node.req.headers });
    if (session) {
      await setUserSession(event, {
        user: {
          id: session.user.id,
          email: session.user.email,
          name: session.user.name,
          provider: session.user.provider,
        },
      });
    }
  }

  return response;
});

4. Generate Database Migration

Generate: migrations/0001_auth.sql

-- Users table
CREATE TABLE users (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  email_verified INTEGER DEFAULT 0,
  password_hash TEXT, -- NULL for OAuth-only
  name TEXT,
  image TEXT,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
);

-- OAuth accounts (if using better-auth)
CREATE TABLE accounts (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  provider TEXT NOT NULL,
  provider_account_id TEXT NOT NULL,
  access_token TEXT,
  refresh_token TEXT,
  expires_at INTEGER,
  created_at TEXT NOT NULL,

  FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
  UNIQUE(provider, provider_account_id)
);

CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_accounts_user ON accounts(user_id);

5. Configure Environment Variables

Generate: .dev.vars

# better-auth secret (generate with: openssl rand -base64 32)
BETTER_AUTH_SECRET=your-32-char-secret-here

# OAuth credentials (if using OAuth providers)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

Production Setup:

wrangler secret put BETTER_AUTH_SECRET
wrangler secret put GOOGLE_CLIENT_SECRET
wrangler secret put GITHUB_CLIENT_SECRET

6. Generate Protected Route Example

Generate: server/api/protected.get.ts

export default defineEventHandler(async (event) => {
  const session = await requireUserSession(event);

  return {
    message: 'Protected data',
    user: session.user,
  };
});

7. Validate Setup

Security Checklist:

  • HTTPS-only cookies configured
  • httpOnly flag set
  • SameSite configured (lax or strict)
  • Password hashing uses Argon2id
  • Session password is 32+ characters
  • OAuth redirect URIs configured (if applicable)
  • CSRF protection enabled (automatic)

Success Criteria

Auth setup complete when:

  • Framework detected and appropriate stack chosen
  • Dependencies installed
  • Configuration files generated
  • Database migration created
  • Environment variables template created
  • Security settings validated
  • Example handlers provided

Output Summary

Files Created:

  • Configuration (app.config.ts or auth.ts)
  • Auth handlers (login, register, logout, OAuth callback)
  • Database migration (users, accounts)
  • Protected route example
  • Environment variables template

Next Actions:

  1. Run database migration
  2. Generate session password (32+ chars)
  3. Configure OAuth providers (if applicable)
  4. Test authentication flow
  5. Add rate limiting to auth endpoints
  6. Deploy with /es-deploy

Notes

  • Always use better-auth for authentication (Workers-optimized)
  • Add OAuth/passkeys/magic links as needed
  • Query better-auth MCP for latest provider requirements
  • Use Argon2id for password hashing (never bcrypt)
  • Store secrets in Cloudflare Workers secrets (not wrangler.toml)
  • See agents/integrations/better-auth-specialist for detailed guidance