Files
agent-fox/docs/superpowers/specs/2026-04-03-login-page-oauth-design.md
2026-04-03 12:55:44 +08:00

5.7 KiB

Login Page Redesign + OAuth Support

Overview

Redesign the login/register pages with a left-right split layout featuring prominent branding, and add Google/GitHub/Apple OAuth login via standard server-side redirect flow.

UI Design

Layout

  • Desktop: 50/50 left-right split
  • Mobile: Brand area hidden or collapsed to compact top banner; form area full-width

Left Panel (Brand Area)

Shared AuthBranding component used by both Login and Register pages.

  • Dark/gradient background (fox-amber → fox-orange gradient from existing CSS variables)
  • Large product icon (~80px SVG fox logo)
  • Product name "AgentFox" (large heading font)
  • Slogan "API Docs for LLMs, Done Right"
  • 3 feature highlights, each with icon + text:
    • "Multi-level API retrieval for minimal token usage"
    • "Import OpenAPI specs in seconds"
    • "Works with any MCP-compatible LLM"

Right Panel (Form Area)

  • Light background, vertically centered
  • Title: "Sign in to your account" (login) / "Create your account" (register)
  • Email + Password inputs (reuse existing input styles)
  • Primary action button
  • Divider: "── or continue with ──"
  • Three OAuth buttons in a row: Google / GitHub / Apple (each with official SVG icon)
  • Footer link: "Don't have an account? Sign up" / "Already have an account? Sign in"

OAuth Architecture

Flow (Standard Server-Side Redirect)

Browser clicks OAuth button
  → GET /api/auth/oauth/:provider
  → Server builds authorization URL with state param, 302 redirects to Provider
  → User authorizes on Provider's page
  → Provider redirects to GET /api/auth/oauth/:provider/callback?code=xxx&state=yyy
  → Server validates state, exchanges code for access_token
  → Server fetches user info (email, name, avatar)
  → Server finds or creates user (see Account Linking below)
  → Server issues JWT (accessToken + refreshToken)
  → Server 302 redirects to frontend /login/callback?accessToken=xxx&refreshToken=xxx

Account Linking Strategy

On OAuth callback, the server resolves the user in this order:

  1. Look up OAuthAccount by (provider, providerAccountId) → if found, use linked User
  2. If no OAuthAccount match, look up User by email → if found, create OAuthAccount linking to existing user
  3. If no User match, create new User (passwordHash=null, name and avatarUrl from provider) + new OAuthAccount

Security

  • CSRF protection: Generate random state parameter per auth request, store in in-memory Map with 10-minute TTL, validate on callback
  • Token delivery: Tokens passed via URL query params; frontend immediately consumes and clears URL
  • Secrets: All client secrets stay server-side; no OAuth SDK loaded in frontend

Provider Configuration

Provider Auth URL Token URL UserInfo URL Scopes
Google accounts.google.com/o/oauth2/v2/auth oauth2.googleapis.com/token www.googleapis.com/oauth2/v2/userinfo email, profile
GitHub github.com/login/oauth/authorize github.com/login/oauth/access_token api.github.com/user + /user/emails user:email
Apple appleid.apple.com/auth/authorize appleid.apple.com/auth/token (decoded from id_token) name, email

Environment Variables

# Already in .env.example
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=

# New
APPLE_CLIENT_ID=
APPLE_TEAM_ID=
APPLE_KEY_ID=
APPLE_PRIVATE_KEY=
OAUTH_CALLBACK_BASE_URL=http://localhost:3000

Frontend Callback Page (/login/callback)

  • Extracts accessToken and refreshToken from URL search params
  • Stores in localStorage, updates AuthContext
  • Redirects to /dashboard (or saved redirect target)
  • Shows loading spinner during processing
  • Shows error message with retry link on failure

File Changes

New Files

File Purpose
packages/server/src/routes/oauth.ts OAuth routes (/:provider, /:provider/callback)
packages/server/src/lib/oauth-providers.ts Provider configs + token exchange + userinfo fetch
packages/web/src/pages/LoginCallback.tsx OAuth callback landing page
packages/web/src/components/AuthBranding.tsx Shared left-panel brand component
packages/web/src/components/OAuthButtons.tsx Third-party login button group

Modified Files

File Change
packages/server/src/index.ts Register /auth/oauth route
packages/web/src/pages/Login.tsx Refactor to left-right split layout
packages/web/src/pages/Register.tsx Refactor to left-right split layout
packages/web/src/App.tsx Add /login/callback route
packages/web/src/lib/i18n.tsx Add translation keys
.env.example Add Apple OAuth env vars

No Changes Needed

  • prisma/schema.prisma — OAuthAccount model already exists
  • JWT signing logic — reuse existing generateAccessToken/generateRefreshToken
  • Existing email/password auth — unchanged

No New Dependencies

  • OAuth token exchange: Node native fetch
  • Apple JWT client_secret signing: Node crypto built-in
  • No Passport.js, no OAuth libraries

User Action Required

Before testing OAuth, the developer must register apps on each provider:

  • Google: Google Cloud Console → OAuth 2.0 Client → redirect URI: {OAUTH_CALLBACK_BASE_URL}/auth/oauth/google/callback
  • GitHub: GitHub Developer Settings → OAuth App → callback URL: {OAUTH_CALLBACK_BASE_URL}/auth/oauth/github/callback
  • Apple: Apple Developer → Services ID + Key → return URL: {OAUTH_CALLBACK_BASE_URL}/auth/oauth/apple/callback (requires HTTPS)

Apple Sign In requires a paid Apple Developer account ($99/year) and HTTPS for callbacks. If unavailable, the Apple button can be displayed as "Coming Soon".