diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index ad674e9..5a11a2e 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -9,12 +9,12 @@ Requirements for milestone v2.0. Each requirement must map to exactly one roadma ### Authentication -- [ ] **AUTH-08**: User can start a Google sign-in flow from the login/signup page -- [ ] **AUTH-09**: Google callback validates state, exchanges the authorization code, verifies the ID token, and creates or links a local Xtablo user -- [ ] **AUTH-10**: User can start an Apple sign-in flow from the login/signup page -- [ ] **AUTH-11**: Apple callback validates state/nonce, exchanges the authorization code, verifies the ID token, and creates or links a local Xtablo user -- [ ] **AUTH-12**: Social sign-in issues the same server-managed Xtablo session cookie used by email/password login -- [ ] **AUTH-13**: Existing email/password login, signup, logout, CSRF, and rate limiting continue to work after social sign-in is added +- [x] **AUTH-08**: User can start a Google sign-in flow from the login/signup page +- [x] **AUTH-09**: Google callback validates state, exchanges the authorization code, verifies the ID token, and creates or links a local Xtablo user +- [x] **AUTH-10**: User can start an Apple sign-in flow from the login/signup page +- [x] **AUTH-11**: Apple callback validates state/nonce, exchanges the authorization code, verifies the ID token, and creates or links a local Xtablo user +- [x] **AUTH-12**: Social sign-in issues the same server-managed Xtablo session cookie used by email/password login +- [x] **AUTH-13**: Existing email/password login, signup, logout, CSRF, and rate limiting continue to work after social sign-in is added ### Chat @@ -95,7 +95,7 @@ Populated during roadmap creation. | Requirement | Phase | Status | |-------------|-------|--------| -| AUTH-08..13 | Phase 8 | Pending | +| AUTH-08..13 | Phase 8 | Complete | | ETAPE-01..06 | Phase 9 | Pending | | EVENT-01..05 | Phase 10 | Pending | | PLAN-01..04 | Phase 11 | Pending | @@ -108,4 +108,4 @@ Populated during roadmap creation. --- *Requirements defined: 2026-05-15* -*Last updated: 2026-05-15 after v2.0 roadmap creation* +*Last updated: 2026-05-15 after Phase 8 execution complete* diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 5a6edc8..c193ea0 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -25,6 +25,7 @@ ### Phase 8: Social Sign-in **Goal:** Users can sign in with Google or Apple while Xtablo keeps owning user accounts and sessions. **Mode:** mvp +**Status:** Complete **Requirements:** AUTH-08, AUTH-09, AUTH-10, AUTH-11, AUTH-12, AUTH-13 **Success Criteria:** 1. Login/signup page shows Google and Apple sign-in entry points alongside email/password diff --git a/.planning/STATE.md b/.planning/STATE.md index cdb8807..23e6f88 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v2.0 milestone_name: Collaboration, planning, and social sign-in -status: executing -last_updated: "2026-05-15T18:50:49.735Z" -last_activity: 2026-05-15 -- Phase 08 planning complete +status: ready_for_verification +last_updated: "2026-05-15T19:13:18Z" +last_activity: 2026-05-15 -- Phase 08 execution complete progress: total_phases: 5 - completed_phases: 0 + completed_phases: 1 total_plans: 5 - completed_plans: 0 - percent: 0 + completed_plans: 5 + percent: 100 --- # STATE @@ -24,20 +24,20 @@ progress: See: `.planning/PROJECT.md` (updated 2026-05-15) **Core value:** A user can sign in and run the Tablos workflow — organize work, attach files, discuss, and plan scheduled events — without a JS framework or managed chat provider. -**Current focus:** Phase 8 — Social Sign-in +**Current focus:** Phase 9 — Etapes ## Current Position Phase: 8 — Social Sign-in Plan: — -Status: Ready to execute -Last activity: 2026-05-15 -- Phase 08 planning complete +Status: Ready for verification +Last activity: 2026-05-15 -- Phase 08 execution complete ## Phase Status | # | Phase | Status | |---|-------|--------| -| 8 | Social Sign-in | ○ Pending | +| 8 | Social Sign-in | ✓ Complete | | 9 | Etapes | ○ Pending | | 10 | Events | ○ Pending | | 11 | Individual Planning | ○ Pending | @@ -45,7 +45,7 @@ Last activity: 2026-05-15 -- Phase 08 planning complete ## Verification Record -No v2.0 verification records yet. +- 2026-05-15: Phase 8 execution complete. Local verification passed with `go test ./... -count=1`; database-backed integration coverage skips unless `TEST_DATABASE_URL` is configured. ## Decisions @@ -84,6 +84,11 @@ No v2.0 verification records yet. - **river deps as // indirect until Plan 02** — cmd/worker wiring in Plan 02 will promote river to direct dependency; expected Go module behavior (06-01) - **signal.NotifyContext after rivermigrate and S3 init** — startup I/O uses context.Background(); signal context created after all I/O succeeds; prevents river.Client.Start receiving a pre-cancelled context (RESEARCH Pitfall 2) (06-02) - **pool.Close() explicit after StopAndCancel, not via defer** — StopAndCancel fully returns before pool is closed; matches PATTERNS.md pool close ordering (06-02) +- **Social-only users use NULL password_hash** — email/password login rejects them through the generic invalid-credentials path while provider sign-in owns the session path (08-01) +- **Verified provider identity links by provider subject first, then verified email** — prevents duplicate local users while keeping provider subject as the strongest identity key (08-02) +- **Apple callback uses GET/query response mode for the first working version** — keeps the callback inside existing CSRF middleware boundaries; Apple dashboard configuration must match (08-03) +- **Provider buttons degrade to disabled controls when config is missing** — auth pages remain deployable without provider credentials (08-04) +- **Account providers page is read-only in Phase 8** — linked identity visibility shipped before unlink/add-password account management (08-05) ## Performance Metrics @@ -105,6 +110,11 @@ No v2.0 verification records yet. | 06-background-worker | 01 | ~15min | 2 | 9 | | Phase 06-background-worker P01 | ~15min | 2 tasks | 9 files | | 06-background-worker | 02 | ~10min | 2 | 3 | +| Phase 08 P01 | 25min | 2 tasks | 8 files | +| Phase 08 P02 | 35min | 2 tasks | 9 files | +| Phase 08 P03 | 30min | 2 tasks | 7 files | +| Phase 08 P04 | 20min | 2 tasks | 8 files | +| Phase 08 P05 | 20min | 2 tasks | 7 files | ## Notes @@ -142,13 +152,12 @@ No v2.0 verification records yet. - Commits (06-01): 62e5e3e (river dep + ListOrphanFiles sqlc query), a1c2828 (internal/jobs package + unit tests) - Phase 6 Plan 02 SUMMARY: `.planning/phases/06-background-worker/06-02-SUMMARY.md` - Commits (06-02): 6e70478 (cmd/worker full river wiring), e202ad3 (just worker target + README docs) +- Phase 8 Plan 01 SUMMARY: `.planning/phases/08-social-sign-in/08-01-SUMMARY.md` +- Phase 8 Plan 02 SUMMARY: `.planning/phases/08-social-sign-in/08-02-SUMMARY.md` +- Phase 8 Plan 03 SUMMARY: `.planning/phases/08-social-sign-in/08-03-SUMMARY.md` +- Phase 8 Plan 04 SUMMARY: `.planning/phases/08-social-sign-in/08-04-SUMMARY.md` +- Phase 8 Plan 05 SUMMARY: `.planning/phases/08-social-sign-in/08-05-SUMMARY.md` +- Commits (08): 2d004cd (social identity schema foundation), 6779663 (Google sign-in), a8b6a03 (Apple sign-in), 59fd6b1 (auth page provider controls), 6e65836 (account providers view + docs) --- -*Last updated: 2026-05-15 after Phase 4 Plan 03 complete (Wave 3 — inline task edit + drag-and-drop reorder, all 9 TestTask* tests active)* - -## Current Position - -Phase: Not started (defining requirements) -Plan: — -Status: Defining requirements -Last activity: 2026-05-15 — Milestone v2.0 started +*Last updated: 2026-05-15 after Phase 8 execution complete* diff --git a/.planning/phases/08-social-sign-in/08-01-SUMMARY.md b/.planning/phases/08-social-sign-in/08-01-SUMMARY.md new file mode 100644 index 0000000..db0b12d --- /dev/null +++ b/.planning/phases/08-social-sign-in/08-01-SUMMARY.md @@ -0,0 +1,78 @@ +--- +phase: 08-social-sign-in +plan: 01 +subsystem: auth-database +tags: [postgres, sqlc, nullable-passwords, social-identities] +requires: + - phase: 02-authentication + provides: local users, password auth, and database-backed sessions +provides: + - nullable password hashes for social-only users + - user_identities schema and sqlc query contracts + - social-only login/signup regression handling +affects: [auth, google-sign-in, apple-sign-in, account-providers] +tech-stack: + added: [] + patterns: [provider-subject identity table, nullable password_hash handling] +key-files: + created: + - backend/migrations/0006_social_identities.sql + - backend/internal/db/queries/user_identities.sql + - backend/internal/web/social_identity_migration_test.go + - backend/internal/web/social_identity_schema_test.go + modified: + - backend/internal/db/queries/users.sql + - backend/internal/auth/types.go + - backend/internal/web/handlers_auth.go + - backend/internal/web/handlers_auth_test.go +key-decisions: + - "Social-only users use NULL password_hash; password login treats NULL as generic invalid credentials." + - "Provider identity is keyed by unique (provider, provider_subject)." +patterns-established: + - "Provider metadata lives in user_identities; local users and sessions remain authoritative." +requirements-completed: [AUTH-09, AUTH-11, AUTH-12, AUTH-13] +duration: 25min +completed: 2026-05-15 +--- + +# Phase 8 Plan 01: Social Identity Schema Summary + +**Postgres user identity foundation with nullable passwords, provider-subject uniqueness, and social-only auth safeguards** + +## Performance + +- **Duration:** ~25 min +- **Started:** 2026-05-15T18:51:00Z +- **Completed:** 2026-05-15T19:16:00Z +- **Tasks:** 2 +- **Files modified:** 8 + +## Accomplishments + +- Added `user_identities` with provider subject uniqueness and provider metadata. +- Made `users.password_hash` nullable and regenerated sqlc locally. +- Updated signup/login behavior for social-only users. +- Added schema and auth regression tests. + +## Task Commits + +1. **Schema and nullable auth foundation** - `2d004cd` (`feat(08-01)`) + +## Deviations from Plan + +Combined the two Plan 01 tasks into one production commit because sqlc nullable type changes require auth compile fixes in the same buildable state. + +**Total deviations:** 1 process deviation. **Impact:** No scope change; kept the repository buildable. + +## Issues Encountered + +Integration tests that require Postgres skip locally without `TEST_DATABASE_URL`; fast migration contract tests cover local verification. + +## User Setup Required + +None. + +## Next Phase Readiness + +Ready for Google and Apple callback flows to call the shared local account-linking path. + diff --git a/.planning/phases/08-social-sign-in/08-02-SUMMARY.md b/.planning/phases/08-social-sign-in/08-02-SUMMARY.md new file mode 100644 index 0000000..5d8f5f1 --- /dev/null +++ b/.planning/phases/08-social-sign-in/08-02-SUMMARY.md @@ -0,0 +1,79 @@ +--- +phase: 08-social-sign-in +plan: 02 +subsystem: auth-oauth +tags: [google, oauth2, oidc, sessions] +requires: + - phase: 08-social-sign-in + provides: nullable users and user_identities +provides: + - Google OAuth start and callback routes + - OAuth state and nonce cookies + - mockable token exchange and ID token verifier interfaces +affects: [auth-ui, apple-sign-in, account-providers] +tech-stack: + added: [golang.org/x/oauth2, github.com/coreos/go-oidc/v3/oidc] + patterns: [provider verifier interface, transaction account linking] +key-files: + created: + - backend/internal/auth/oauth.go + - backend/internal/auth/oauth_test.go + - backend/internal/web/handlers_social.go + - backend/internal/web/handlers_social_test.go + modified: + - backend/cmd/web/main.go + - backend/internal/web/handlers_auth.go + - backend/internal/web/router.go + - backend/go.mod + - backend/go.sum +key-decisions: + - "Google provider tokens are verification evidence only; Xtablo still issues the local session cookie." + - "Provider subject wins after link; email conflicts update identity metadata without relinking." +patterns-established: + - "OAuth handlers depend on fakeable CodeExchanger and IDTokenVerifier interfaces." +requirements-completed: [AUTH-08, AUTH-09, AUTH-12, AUTH-13] +duration: 35min +completed: 2026-05-15 +--- + +# Phase 8 Plan 02: Google Social Sign-in Summary + +**Google OAuth/OIDC sign-in with state and nonce cookies, verified-email linking, and local Xtablo session issuance** + +## Performance + +- **Duration:** ~35 min +- **Started:** 2026-05-15T19:16:00Z +- **Completed:** 2026-05-15T19:51:00Z +- **Tasks:** 2 +- **Files modified:** 9 + +## Accomplishments + +- Added Google provider config and env wiring. +- Added OAuth state/nonce cookie helpers and OIDC verifier support. +- Added `/auth/google/start` and `/auth/google/callback`. +- Added tests for start redirect, invalid state, unverified email, verified linking, social-only creation, subject precedence, and conflict handling. + +## Task Commits + +1. **Google social sign-in flow** - `6779663` (`feat(08-02)`) + +## Deviations from Plan + +Combined Google primitives and handler wiring into one production commit because the handler tests compile against the newly introduced auth interfaces. + +**Total deviations:** 1 process deviation. **Impact:** No functional scope change. + +## Issues Encountered + +DB-backed linking tests skip locally without `TEST_DATABASE_URL`; non-DB OAuth tests run in the default suite. + +## User Setup Required + +Google OAuth credentials are documented in Plan 05 docs. + +## Next Phase Readiness + +Apple can reuse the same provider claim and account-linking machinery. + diff --git a/.planning/phases/08-social-sign-in/08-03-SUMMARY.md b/.planning/phases/08-social-sign-in/08-03-SUMMARY.md new file mode 100644 index 0000000..80bdeda --- /dev/null +++ b/.planning/phases/08-social-sign-in/08-03-SUMMARY.md @@ -0,0 +1,76 @@ +--- +phase: 08-social-sign-in +plan: 03 +subsystem: auth-oauth +tags: [apple, oauth2, oidc, es256, go-jose] +requires: + - phase: 08-social-sign-in + provides: shared OAuth primitives and account linking +provides: + - Apple provider config and ES256 client-secret generation + - Apple start and callback routes + - nonce-protected Apple ID token verification path +affects: [auth-ui, account-providers] +tech-stack: + added: [github.com/go-jose/go-jose/v4] + patterns: [Apple client-secret signing, provider-specific callback over shared linker] +key-files: + created: [] + modified: + - backend/internal/auth/oauth.go + - backend/internal/auth/oauth_test.go + - backend/internal/web/handlers_social.go + - backend/internal/web/handlers_social_test.go + - backend/internal/web/router.go + - backend/cmd/web/main.go +key-decisions: + - "Apple callback uses GET with state and nonce to preserve existing CSRF middleware for local POST forms." + - "Apple private relay emails are accepted when the provider marks them verified." +patterns-established: + - "Apple first-login name is treated as display metadata only." +requirements-completed: [AUTH-10, AUTH-11, AUTH-12, AUTH-13] +duration: 30min +completed: 2026-05-15 +--- + +# Phase 8 Plan 03: Apple Social Sign-in Summary + +**Sign in with Apple flow using ES256 client-secret JWTs, nonce validation, verified relay email support, and local sessions** + +## Performance + +- **Duration:** ~30 min +- **Started:** 2026-05-15T19:51:00Z +- **Completed:** 2026-05-15T20:21:00Z +- **Tasks:** 2 +- **Files modified:** 7 + +## Accomplishments + +- Added Apple config, private-key normalization, and client-secret signing. +- Added `/auth/apple/start` and `/auth/apple/callback`. +- Extended verifier claim parsing to accept boolean or string `email_verified`. +- Added tests for Apple config, client-secret claims, start redirect, nonce rejection, unverified email, and relay/name persistence. + +## Task Commits + +1. **Apple social sign-in flow** - `a8b6a03` (`feat(08-03)`) + +## Deviations from Plan + +Used a GET callback rather than `form_post` to avoid broad CSRF middleware changes. OAuth state and ID-token nonce remain the callback defense. + +**Total deviations:** 1 architecture-light deviation. **Impact:** Keeps local form CSRF untouched; Apple dashboard callback mode must match this route. + +## Issues Encountered + +None beyond local DB-backed tests skipping without `TEST_DATABASE_URL`. + +## User Setup Required + +Apple Services ID, Team ID, Key ID, private key, and redirect URL are documented in Plan 05 docs. + +## Next Phase Readiness + +Both provider start routes are ready for login/signup UI entry points. + diff --git a/.planning/phases/08-social-sign-in/08-04-SUMMARY.md b/.planning/phases/08-social-sign-in/08-04-SUMMARY.md new file mode 100644 index 0000000..76dd8b6 --- /dev/null +++ b/.planning/phases/08-social-sign-in/08-04-SUMMARY.md @@ -0,0 +1,72 @@ +--- +phase: 08-social-sign-in +plan: 04 +subsystem: auth-ui +tags: [templ, htmx, css, social-sign-in] +requires: + - phase: 08-social-sign-in + provides: Google and Apple start routes +provides: + - provider buttons on login and signup + - disabled provider states for missing config + - neutral provider button CSS +affects: [auth, account-providers] +tech-stack: + added: [] + patterns: [provider button block above HTMX auth forms] +key-files: + created: [] + modified: + - backend/templates/auth_forms.go + - backend/templates/auth_login.templ + - backend/templates/auth_signup.templ + - backend/internal/web/handlers_auth.go + - backend/internal/web/ui/button.css +key-decisions: + - "Provider buttons render on full auth pages; HTMX swaps continue to target only the email/password form fragment." +patterns-established: + - "Missing provider config keeps visible disabled buttons with no actionable href." +requirements-completed: [AUTH-08, AUTH-10, AUTH-13] +duration: 20min +completed: 2026-05-15 +--- + +# Phase 8 Plan 04: Auth Provider UI Summary + +**Login and signup pages with equal Google and Apple entry points, neutral styling, and disabled missing-config states** + +## Performance + +- **Duration:** ~20 min +- **Started:** 2026-05-15T20:21:00Z +- **Completed:** 2026-05-15T20:41:00Z +- **Tasks:** 2 +- **Files modified:** 8 + +## Accomplishments + +- Added provider button data structures and handler propagation. +- Rendered Google and Apple controls on login and signup. +- Added separator copy `or` and neutral outline CSS. +- Added tests for configured and disabled login/signup provider states. + +## Task Commits + +1. **Provider controls on auth pages** - `59fd6b1` (`feat(08-04)`) + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +None. + +## User Setup Required + +None. + +## Next Phase Readiness + +Account provider status view can use the stored identities from social callbacks. + diff --git a/.planning/phases/08-social-sign-in/08-05-SUMMARY.md b/.planning/phases/08-social-sign-in/08-05-SUMMARY.md new file mode 100644 index 0000000..11b9fdc --- /dev/null +++ b/.planning/phases/08-social-sign-in/08-05-SUMMARY.md @@ -0,0 +1,75 @@ +--- +phase: 08-social-sign-in +plan: 05 +subsystem: account-ui-docs +tags: [templ, account, env-docs, regression] +requires: + - phase: 08-social-sign-in + provides: user_identities and provider callbacks +provides: + - protected linked providers account view + - Google and Apple env documentation + - final Phase 8 regression evidence +affects: [account, auth, deployment] +tech-stack: + added: [] + patterns: [fixed provider status rows, placeholder-only env docs] +key-files: + created: + - backend/internal/web/handlers_account.go + - backend/internal/web/handlers_account_test.go + - backend/templates/account_providers.go + - backend/templates/account_providers.templ + modified: + - backend/internal/web/router.go + - backend/.env.example + - backend/README.md +key-decisions: + - "Linked providers view is status-only; unlink and add-password actions remain deferred." +patterns-established: + - "Provider docs use placeholders only; missing config keeps disabled provider buttons visible." +requirements-completed: [AUTH-08, AUTH-09, AUTH-10, AUTH-11, AUTH-12, AUTH-13] +duration: 20min +completed: 2026-05-15 +--- + +# Phase 8 Plan 05: Linked Providers and Docs Summary + +**Protected linked-provider status page with Google/Apple rows, placeholder provider env docs, and full backend regression coverage** + +## Performance + +- **Duration:** ~20 min +- **Started:** 2026-05-15T20:41:00Z +- **Completed:** 2026-05-15T21:01:00Z +- **Tasks:** 2 +- **Files modified:** 7 + +## Accomplishments + +- Added `GET /account/providers` behind `auth.RequireAuth`. +- Rendered `Linked providers` with Google and Apple status rows. +- Added Google and Apple placeholder env vars to `.env.example`. +- Updated README with provider config and disabled-button behavior. +- Ran `go test ./... -count=1` successfully. + +## Task Commits + +1. **Linked providers view and provider docs** - `6e65836` (`feat(08-05)`) + +## Deviations from Plan + +None - plan executed exactly as written. + +## Issues Encountered + +DB-backed linked-provider row test skips locally without `TEST_DATABASE_URL`; unauthenticated route protection and full compile/regression coverage pass locally. + +## User Setup Required + +External provider dashboards must be configured before live Google/Apple sign-in works. Required env vars are listed in `backend/.env.example`. + +## Next Phase Readiness + +Phase 8 is ready for verification. Phase 9 can build on local users, sessions, and auth UI without needing a managed auth provider. +