diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index d7372b8..b729289 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -189,7 +189,7 @@ Plans: | 12. Comparison View | v1.3 | 1/1 | Complete | 2026-03-17 | | 13. Setup Impact Preview | v1.3 | 0/2 | Not started | - | | 14. PostgreSQL Migration | v2.0 | 0/? | Not started | - | -| 15. External Authentication | v2.0 | 1/1 | Complete | 2026-04-04 | +| 15. External Authentication | v2.0 | 1/1 | Complete | 2026-04-04 | | 16. Multi-User Data Model | v2.0 | 0/? | Not started | - | | 17. Object Storage | v2.0 | 0/? | Not started | - | | 18. Global Items & Public Profiles | v2.0 | 0/? | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index f0643be..ac47110 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -4,13 +4,13 @@ milestone: v1.3 milestone_name: Research & Decision Tools status: planning stopped_at: Completed 15-03-PLAN.md -last_updated: "2026-04-04T18:55:48.924Z" -last_activity: 2026-04-03 — v2.0 roadmap created (Phases 14-18) +last_updated: "2026-04-04T19:52:25.447Z" +last_activity: 2026-04-04 progress: - total_phases: 8 - completed_phases: 7 - total_plans: 13 - completed_plans: 11 + total_phases: 9 + completed_phases: 8 + total_plans: 21 + completed_plans: 19 percent: 0 --- @@ -25,10 +25,10 @@ See: .planning/PROJECT.md (updated 2026-04-03) ## Current Position -Phase: 14 of 18 (PostgreSQL Migration) -Plan: 0 of ? in current phase +Phase: 15 of 18 (PostgreSQL Migration) +Plan: Not started Status: Ready to plan -Last activity: 2026-04-03 — v2.0 roadmap created (Phases 14-18) +Last activity: 2026-04-04 Progress: [----------] 0% (v2.0 milestone) diff --git a/.planning/phases/15-external-authentication/15-VERIFICATION.md b/.planning/phases/15-external-authentication/15-VERIFICATION.md new file mode 100644 index 0000000..c4994bf --- /dev/null +++ b/.planning/phases/15-external-authentication/15-VERIFICATION.md @@ -0,0 +1,167 @@ +--- +phase: 15-external-authentication +verified: 2026-04-04T19:30:00Z +status: passed +score: 12/12 must-haves verified +re_verification: false +--- + +# Phase 15: External Authentication Verification Report + +**Phase Goal:** Users can register and log in via a self-hosted OIDC auth provider, replacing the built-in single-user auth system +**Verified:** 2026-04-04T19:30:00Z +**Status:** PASSED +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths + +All truths are drawn from must_haves across the three plan files. + +#### Plan 01 Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 1 | Logto container starts alongside Postgres in docker-compose | VERIFIED | `docker-compose.yml` lines 17-31 and `docker-compose.dev.yml` lines 19-33 both define `svhd/logto:latest` service with `depends_on: postgres: condition: service_healthy` | +| 2 | Logto admin console is accessible at port 3002 | VERIFIED | Both compose files expose ports `"3001:3001"` and `"3002:3002"` | +| 3 | A separate logto database is created automatically on Postgres first boot | VERIFIED | `docker/init-logto-db.sql` contains `CREATE DATABASE logto;`, mounted to `docker-entrypoint-initdb.d` in both compose files | +| 4 | GearBox schema no longer contains users or sessions tables | VERIFIED | `src/db/schema.ts` has no `export const users` or `export const sessions`; migration `drizzle/0010_foamy_marvel_zombies.sql` drops both tables | +| 5 | All OIDC env vars documented | VERIFIED | `.env.example` contains `LOGTO_ENDPOINT`, `LOGTO_CLIENT_ID`, `LOGTO_CLIENT_SECRET`, `OIDC_AUTH_SECRET` | + +#### Plan 02 Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 6 | requireAuth middleware validates API keys, MCP Bearer tokens, and OIDC session cookies | VERIFIED | `src/server/middleware/auth.ts` checks `X-API-Key` → `Authorization: Bearer` → `getAuth(c)` in that order; no `getUserCount` bypass | +| 7 | GET /login redirects unauthenticated users to Logto | VERIFIED | `src/server/index.ts` line 44: `app.get("/login", oidcAuthMiddleware(), async (c) => c.redirect("/"))` | +| 8 | GET /callback processes the OIDC authorization code and sets a session cookie | VERIFIED | `src/server/index.ts` line 45: `app.get("/callback", async (c) => processOAuthCallback(c))` | +| 9 | GET /api/auth/me returns user identity from OIDC claims or null | VERIFIED | `src/server/routes/auth.ts` uses `getAuth(c)` and returns `{ user: { id: auth.sub, email: auth.email }, authenticated: true }` or `{ user: null, authenticated: false }` | +| 10 | API keys continue to authenticate programmatic requests | VERIFIED | `verifyApiKey` first path in `requireAuth`; `auth.service.ts` retains all four API key functions | +| 11 | MCP OAuth /oauth/authorize validates via OIDC session instead of username/password | VERIFIED | `src/server/routes/oauth.ts` GET and POST `/authorize` both call `getAuth(c)`, redirect to `/login` if null; no `verifyPassword` reference anywhere | + +#### Plan 03 Truths + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 12 | Login page redirects users to Logto instead of showing a credential form | VERIFIED | `src/client/routes/login.tsx` has no `