Skip to content

Instantly share code, notes, and snippets.

@drewstone
Created February 4, 2026 06:20
Show Gist options
  • Select an option

  • Save drewstone/ba0406637d601b190a374a917ba4534c to your computer and use it in GitHub Desktop.

Select an option

Save drewstone/ba0406637d601b190a374a917ba4534c to your computer and use it in GitHub Desktop.
Blueprint Agent E2E Auth Audit - Feb 3, 2026

E2E Auth Audit — Feb 3, 2026

Problem Summary

E2E tests fail because:

  1. OAuth is the PRIMARY auth method — GitHub button shown first
  2. Email/password is HIDDEN — requires clicking "Other sign-in options"
  3. Tests need to expand "Other sign-in options" before filling email form

Current Auth Flow Analysis

SignInModal.tsx → AuthForm.tsx

┌─────────────────────────────────────┐
│    Welcome back / Create account    │
├─────────────────────────────────────┤
│ [  Continue with GitHub  ]  ← PRIMARY│
│                                     │
│     ▶ Other sign-in options         │
│                                     │
│ ─────── COLLAPSED ───────           │
│ │ [ Continue with Google ]        │ │
│ │ [ Continue with Twitter ]       │ │
│ │ [Sign In with Ethereum]         │ │
│ │                                 │ │
│ │ ─── or ───                      │ │
│ │                                 │ │
│ │ [Email input]                   │ │
│ │ [Password input]                │ │
│ │ [Submit button]                 │ │
│ ─────────────────────────────────── │
└─────────────────────────────────────┘

Root Cause

The test-helpers.ts already handles this flow:

// Click "Other sign-in options" to expand
await dialog.locator('button').filter({ hasText: /other sign/i }).first().click();

BUT the issue is:

  1. Email verification required in production moderequireEmailVerification: !isNonProd
  2. APP_ENV detection — If APP_ENV is not set or invalid, falls back to 'local'
  3. Current .dev.vars has APP_ENV=development which is NOT a valid value

Valid APP_ENV values (from common.ts):

  • 'local' ← what tests should use
  • 'develop'
  • 'staging'
  • 'production'
  • 'test'IDEAL for E2E

Recommended Fix: APP_ENV=test mode

Option A: Create test-specific auth bypass (RECOMMENDED)

1. Add APP_ENV=test support in auth.server.tsx:

// In getAuthOptions():
const isNonProd = isNonProduction(env);
const isTestEnv = isTest(env);  // Add this

emailAndPassword: {
  enabled: true,
  // In test mode, skip email verification entirely
  requireEmailVerification: !isNonProd && !isTestEnv,
  ...
},

2. Create E2E test user seeding:

// scripts/e2e/seed-test-user.ts
import { drizzle } from 'drizzle-orm/...';
import { users } from '~/lib/.server/db/betterAuthSchema';

async function seedTestUser() {
  const db = drizzle(...);
  
  // Create or update test user with verified email
  await db.insert(users).values({
    id: 'e2e-test-user-id',
    email: 'test-e2e@example.com',
    name: 'E2E Test User',
    emailVerified: true,  // Pre-verified!
    // ... hash password
  }).onConflictDoUpdate({
    target: users.email,
    set: { emailVerified: true }
  });
}

3. Update .dev.vars for E2E:

APP_ENV=test  # Not 'development'!

Option B: Playwright storageState (session persistence)

Save auth state after first login:

// e2e/setup/auth.setup.ts
import { test as setup } from '@playwright/test';

setup('authenticate', async ({ page }) => {
  await page.goto('/');
  // ... login flow
  await page.context().storageState({ path: '.auth/user.json' });
});

Then reuse in tests:

// playwright.config.ts
export default defineConfig({
  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'e2e',
      use: { storageState: '.auth/user.json' },
      dependencies: ['setup'],
    },
  ],
});

Option C: API endpoint for test-only login

Add a test-only endpoint that bypasses OAuth:

// routes/api.test.login.ts (only enabled when APP_ENV=test)
export async function action({ request, context }: ActionFunctionArgs) {
  if (!isTest(context.cloudflare.env)) {
    throw new Response('Not found', { status: 404 });
  }
  
  const { email, password } = await request.json();
  // Directly create session without OAuth/email verification
  const session = await createTestSession(email, password);
  return json({ token: session.token });
}

Implementation Priority

Fix Effort Reliability Recommendation
APP_ENV=test + pre-verified user Low High DO THIS
Playwright storageState Medium High ✅ Also do
Test-only login endpoint Medium High Optional
Full OAuth automation High Low ❌ Don't bother

Missing User Stories (from MISSING-USER-STORIES.md)

CRITICAL (0% coverage):

  1. Billing/Credits — No E2E tests at all
  2. Settings/Profile — No E2E tests at all
  3. Partner Features — No E2E tests at all
  4. Admin Features — No E2E tests at all

Current coverage: 16% (15/94 user stories)

Priority P0 missing:

  • User views credit balance
  • User purchases credits
  • User adds payment method
  • User signs out
  • User creates/edits files in editor
  • User runs terminal commands

Immediate Action Items

  1. Fix APP_ENV — Change .dev.vars from development to local or test

  2. Seed test user — Create pre-verified user in database:

    INSERT INTO users (id, email, name, email_verified, created_at)
    VALUES ('e2e-test', 'test-e2e@example.com', 'E2E Test', true, NOW())
    ON CONFLICT (email) DO UPDATE SET email_verified = true;
  3. Update test-helpers.ts — Add retry logic for auth flow

  4. Add Playwright auth setup — Persist session for faster tests


Network Disconnect Bug (Separate Issue)

From stress-edge-cases test:

  • When network disconnects during streaming and reconnects, auth state is LOST
  • User gets redirected to OAuth login
  • This is a session persistence bug, not auth flow bug

Fix: Better session recovery / reconnection handling in ConnectionStateMachine.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment