# S02 Research: [A] Creator Authentication + Dashboard Shell

## Summary

This is a greenfield auth system for an existing FastAPI + React codebase that has zero authentication today. The slice adds: a User model (separate from Creator), invite-code-gated registration, JWT-based login sessions, role-based access (creator/admin), and a creator dashboard shell page. The existing Creator model has no auth fields — users will be a new entity linked to creators via foreign key.

**Depth: Targeted.** Auth with PyJWT + bcrypt on FastAPI is well-understood, but this codebase has never had auth, so the integration surface is broad: new models, new migration, new middleware, new API router, new frontend pages, auth context, and protected routes.

## Recommendation

**Stack:** PyJWT (trust 9.9, benchmark 92.5) for JWT encode/decode, passlib[bcrypt] for password hashing. Both are the standard FastAPI auth libraries — no need for heavier frameworks (FastAPI-Users, etc.) given the simple role model.

**Architecture:**
- New `User` model in `backend/models.py` with email, hashed_password, role enum (creator/admin), and optional `creator_id` FK.
- New `InviteCode` model for invite-gated registration (code, uses_remaining, created_by, expires_at).
- `backend/routers/auth.py` with POST /register, POST /login, GET /me, PUT /me (profile settings).
- `backend/auth.py` utility module: password hashing, JWT creation/verification, FastAPI dependency `get_current_user`.
- Frontend: auth context provider, login/register pages, protected route wrapper, dashboard shell page with sidebar nav.
- Existing routes remain public. Only new `/creator/*` and `/admin/*` API routes require auth.

## Implementation Landscape

### Existing Code (Key Files)

| File | Lines | Relevance |
|------|-------|-----------|
| `backend/models.py` | ~520 | All 13 models in one file. Add User + InviteCode here. Site audit warns about splitting but that's S05's job. |
| `backend/schemas.py` | ~500 | Pydantic schemas. Add auth request/response schemas here. |
| `backend/database.py` | ~30 | Async engine + session factory. No changes needed. |
| `backend/config.py` | ~90 | Settings via pydantic-settings. Add JWT_SECRET_KEY, JWT_EXPIRY_MINUTES, INVITE_CODES config. `app_secret_key` already exists but is unused — repurpose for JWT signing. |
| `backend/main.py` | ~100 | FastAPI app setup. Register new auth router here. |
| `backend/routers/` | 10 files | Existing pattern: APIRouter with prefix, Depends(get_session). Auth router follows same pattern. |
| `frontend/src/App.tsx` | ~140 | React Router setup. Add /login, /register, /creator/dashboard routes. |
| `frontend/src/api/public-client.ts` | ~860 | All API calls. `request<T>` function is the injection point for Authorization headers. |
| `frontend/src/App.css` | 5820 | Monolithic CSS. Site audit recommends CSS modules for new Phase 2 components — use `.p2-` namespace prefix at minimum. |
| `alembic/versions/` | 15 migrations | Migration 016 will add users + invite_codes tables. |

### Data Model Design

```
User
  id: UUID (PK)
  email: String(255), unique, not null
  hashed_password: String(255), not null
  display_name: String(255), not null
  role: Enum('creator', 'admin'), not null, default 'creator'
  creator_id: UUID FK -> creators.id, nullable (linked after registration)
  is_active: Boolean, default true
  created_at: DateTime
  updated_at: DateTime

InviteCode
  id: UUID (PK)
  code: String(50), unique, not null
  uses_remaining: Integer, default 1
  created_by: UUID FK -> users.id, nullable (null for seed codes)
  expires_at: DateTime, nullable
  created_at: DateTime
```

**Key design decisions for planner:**
- User is separate from Creator. A Creator is content data (extracted from videos); a User is an account. The `creator_id` FK links them after registration. This preserves the existing Creator model untouched.
- Role enum is minimal: `creator` and `admin`. No `user` role yet — public visitors don't need accounts in Phase 2.
- `is_active` enables account deactivation without deletion.
- InviteCode tracks remaining uses, not a boolean — supports multi-use codes for batch invites.

### Auth Flow

1. **Registration:** POST /api/v1/auth/register — requires valid invite code + email + password + display_name. Creates User, decrements invite code uses. If role is creator, optionally links to existing Creator row by slug or creates new one.
2. **Login:** POST /api/v1/auth/login — email + password → JWT access token (HS256, signed with `app_secret_key`). Token payload: `{sub: user_id, role: role, exp: now + JWT_EXPIRY_MINUTES}`.
3. **Session:** Frontend stores JWT in localStorage. `request<T>` injects `Authorization: Bearer <token>` for authenticated endpoints. No refresh tokens for MVP — just re-login on expiry.
4. **Auth dependency:** `get_current_user(token: str = Depends(oauth2_scheme))` decodes JWT, loads User from DB, returns it. `require_role("admin")` sub-dependency for admin-only routes.

### Frontend Architecture

- **AuthContext** (`frontend/src/context/AuthContext.tsx`): React context with `user`, `token`, `login()`, `register()`, `logout()`, `isAuthenticated`. Wraps the app in App.tsx.
- **Login page** (`/login`): Email + password form. Redirects to dashboard on success.
- **Register page** (`/register`): Invite code + email + password + display name form. Redirects to login on success.
- **Dashboard shell** (`/creator/dashboard`): Left sidebar nav (Dashboard, Content, Settings), main content area. Initially empty "Welcome" content — M020/S02 fills in real analytics.
- **Profile settings** (`/creator/settings`): Display name, email, password change form.
- **ProtectedRoute** component: Wraps creator/admin routes, redirects to /login if not authenticated.
- **Nav update**: Show user avatar/name in header when logged in, with dropdown for Dashboard/Logout.

### CSS Strategy

Per site audit recommendation, new Phase 2 components should use CSS modules or at minimum a `p2-` namespace prefix. The dashboard shell is a good boundary for this. Options:
1. **CSS Modules** (`.module.css` files) — requires no Vite config change (Vite supports them natively). Cleanest isolation.
2. **Namespaced classes** in App.css — consistent with existing pattern but adds to the 5820-line monolith.

Recommend CSS Modules for all new dashboard/auth pages. Existing public pages stay in App.css.

### Migration Plan

Single Alembic migration `016_add_users_and_invite_codes.py`:
- Create `users` table
- Create `invite_codes` table
- No FK from `creators` → `users` (the link is `users.creator_id → creators.id`, one-directional)

### Dependencies to Add

```
# backend/requirements.txt additions:
PyJWT>=2.8,<3.0
passlib[bcrypt]>=1.7,<2.0
```

### Verification Strategy

1. **Backend:** Integration tests in `backend/tests/test_auth.py`:
   - Register with valid invite code → 201 + user created
   - Register with invalid/expired invite code → 403
   - Register duplicate email → 409
   - Login with correct credentials → 200 + JWT
   - Login with wrong password → 401
   - GET /me with valid token → user profile
   - GET /me with expired/invalid token → 401
   - Protected endpoint without token → 401
2. **Frontend:** `npm run build` passes with zero TypeScript errors.
3. **End-to-end:** Register → login → see dashboard shell → navigate to settings. Verify via browser tools or curl.

### Natural Task Seams

1. **Backend models + migration** — User model, InviteCode model, role enum, Alembic migration. Independent, unblocks everything else.
2. **Auth utilities + router** — password hashing, JWT functions, auth dependencies, POST /register, POST /login, GET /me, PUT /me. Depends on (1).
3. **Seed invite codes** — CLI command or migration data to create initial invite codes for testing.
4. **Frontend auth context + login/register pages** — AuthContext, login form, register form, token storage. Depends on (2) for API contract.
5. **Dashboard shell + protected routes** — Dashboard page, sidebar nav, profile settings page, ProtectedRoute component, nav auth state. Depends on (4).

### Risks

| Risk | Impact | Mitigation |
|------|--------|------------|
| JWT secret key rotation | Invalidates all sessions | Use `app_secret_key` from config.py (already exists). Document rotation procedure. |
| Password hashing performance in async context | bcrypt is CPU-bound, blocks event loop | passlib's bcrypt is fast enough for low-volume registration. Use `run_in_executor` if needed — defer until measured. |
| Creator-User linking | Which creator does a new user map to? | Registration accepts optional `creator_slug`. Admin can link/unlink later. |
| localStorage XSS risk | Token theft via XSS | Acceptable for self-hosted single-admin tool. httpOnly cookies would be more secure but add CSRF complexity. Revisit if the tool becomes public-facing. |

### Skill Discovery

No installed skills are directly relevant to FastAPI auth implementation. Searched `npx skills find "fastapi authentication"` — top results had 27-84 installs with narrow scope (JWT bridge, router generation). Not worth installing — the auth pattern is standard and well-understood.
