Replace hand-rolled auth with Ory Kratos browser flows

The honeyDue Go API no longer owns identity — Ory Kratos at
NEXT_PUBLIC_KRATOS_URL does. Rewrite the web app's auth layer to use Kratos
browser self-service flows and the ory_kratos_session cookie.

- Kratos client (src/lib/kratos/): flow init/fetch/submit, whoami, logout,
  message helpers, and the useKratosFlow lifecycle hook.
- Generic flow renderer (src/components/auth/): KratosFlowForm renders
  ui.nodes (inputs, oidc social buttons, hidden csrf), KratosMessages
  surfaces flow-level messages, AuthGate guards /app via whoami.
- Auth pages (login/register/forgot-password/verify-email/reset-password)
  rewritten as Kratos login/registration/recovery/verification/settings
  flows. Password change in settings now uses the Kratos settings flow.
- Proxy + serverFetch forward the ory_kratos_session cookie to the Go API
  instead of "Authorization: Token". Deleted /api/auth/{login,logout,me}.
- Middleware does a cheap ory_kratos_session cookie pre-filter; AuthGate's
  whoami call is authoritative.
- auth store rewritten around whoami + GET /auth/me; removed dead auth API
  functions, types/auth, validations/auth, code-input.
- Added NEXT_PUBLIC_KRATOS_URL to config (.env.example) and CLAUDE.md.

npm run build passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-05-18 18:16:49 -05:00
parent f77f913ee8
commit 42e7bedea4
33 changed files with 1474 additions and 1504 deletions
+17 -4
View File
@@ -32,17 +32,27 @@ npm run analyze # Bundle analysis
```
Browser → Next.js page (client component)
→ apiFetch("/tasks/") → /api/proxy/tasks (Next.js route handler)
→ Go API (reads honeydue-token httpOnly cookie, forwards as Authorization header)
→ Go API (reads ory_kratos_session cookie, forwards it as a Cookie header)
```
Auth tokens are stored as httpOnly cookies (`honeydue-token`), never exposed to JS. The Next.js `/api/proxy/[...path]` catch-all route forwards requests to the Go API.
Identity is owned by **Ory Kratos** (`NEXT_PUBLIC_KRATOS_URL`). The browser holds an `ory_kratos_session` cookie set by Kratos. The Next.js `/api/proxy/[...path]` catch-all route forwards that cookie to the Go API, which validates the session against Kratos. The Go API no longer does auth (no `Authorization: Token`).
### Auth (Ory Kratos)
Login / registration / recovery (forgot-password) / email verification / password changes use **Kratos browser self-service flows**:
- Auth pages (`src/app/(auth)/...`) initialize a flow by hard-navigating the browser to `{kratos}/self-service/{type}/browser`; Kratos sets a flow cookie and redirects back with `?flow=<id>`.
- The page reads `?flow=<id>`, fetches the flow definition (`ui.nodes` / `ui.action` / `ui.method`), and renders it generically via `<KratosFlowForm>`.
- Social sign-in (Apple/Google) = the `oidc` nodes in the flow.
- `src/lib/kratos/` holds the client (`getFlow`, `submitFlow`, `whoami`, `logout`, ...) and the `useKratosFlow` hook.
- Route protection: `<AuthGate>` (in `src/app/app/layout.tsx`) calls `{kratos}/sessions/whoami`; the middleware does a cheap `ory_kratos_session` cookie pre-filter.
### Directory Structure
```
src/
├── app/
│ ├── (auth)/ # Login, register, forgot-password (public)
│ ├── (auth)/ # Kratos self-service flow pages (login/register/recovery/verify)
│ ├── api/proxy/ # Catch-all proxy to Go API
│ ├── app/ # Authenticated app pages
│ │ ├── contractors/ # Contractor CRUD
@@ -103,7 +113,7 @@ src/
**Kanban boards**: Tasks display in kanban columns (overdue, due_soon, in_progress, not_started, completed). Uses `@dnd-kit` for drag-and-drop. Column names match Go API: `overdue_tasks`, `due_soon_tasks`, `in_progress_tasks`, `not_started_tasks`, `completed_tasks`.
**Middleware** (`src/middleware.ts`): Checks `honeydue-token` cookie. Redirects unauthenticated users to `/login` for protected routes. Skips API routes, static files, and public paths.
**Middleware** (`src/middleware.ts`): Cheap pre-filter on the `ory_kratos_session` cookie. Redirects users without the cookie to `/login` for protected routes. Skips API routes, static files, and public paths. The authoritative session check is `<AuthGate>` (`whoami`).
## Conventions
@@ -125,9 +135,12 @@ src/
|----------|-------------|---------|
| `NEXT_PUBLIC_API_URL` | Go API URL (client-side) | `https://honeyDue.treytartt.com/api` |
| `API_URL` | Go API URL (server-side, no proxy) | Falls back to `NEXT_PUBLIC_API_URL` |
| `NEXT_PUBLIC_KRATOS_URL` | Ory Kratos public API URL (identity) | `https://auth.myhoneydue.com` |
| `NEXT_PUBLIC_POSTHOG_KEY` | PostHog analytics key | — |
| `NEXT_PUBLIC_POSTHOG_HOST` | PostHog host | — |
See `.env.example` for the full list.
## Common Tasks
**Add a new page**: Create `src/app/app/{route}/page.tsx` (client component). Add nav item to `src/components/layout/nav-items.ts`.