137 lines
9 KiB
Markdown
137 lines
9 KiB
Markdown
# Phase 8: Social Sign-in - Context
|
|
|
|
**Gathered:** 2026-05-15
|
|
**Status:** Ready for planning
|
|
|
|
<domain>
|
|
## Phase Boundary
|
|
|
|
Add Google and Apple sign-in to the existing Go + HTMX auth system. Provider login must create or link local Xtablo users, then issue the same server-managed session cookie used by email/password auth.
|
|
|
|
Delivers AUTH-08, AUTH-09, AUTH-10, AUTH-11, AUTH-12, AUTH-13. **Not in scope:** managed auth platforms, password reset/add-password flows, provider unlinking, MFA, passkeys, or broader account management.
|
|
|
|
</domain>
|
|
|
|
<decisions>
|
|
## Implementation Decisions
|
|
|
|
### Account Linking
|
|
- **D-01:** If Google/Apple returns a verified email matching an existing Xtablo user, automatically link the provider identity to that user and sign them in.
|
|
- **D-02:** If provider email is missing or not verified, reject the callback with a clear error. Do not create or link an account from unverified/missing provider email.
|
|
- **D-03:** Once a provider subject is linked, provider identity wins on future callbacks. Sign in the already-linked Xtablo user even if the provider email later changes.
|
|
- **D-04:** Phase 8 includes a simple linked providers account view showing Google/Apple connection status. Provider unlink actions are deferred unless planning finds they are trivial and low-risk.
|
|
|
|
### Social-Only Accounts
|
|
- **D-05:** Allow passwordless local users by making `users.password_hash` nullable. Email/password login only applies to users whose `password_hash` is present.
|
|
- **D-06:** Social-only users can exist without a password. Adding a password later is deferred beyond Phase 8.
|
|
- **D-07:** If email/password signup is attempted for an email that already belongs to a social-only user, block signup and show a clear message asking the user to sign in with the provider.
|
|
- **D-08:** Existing email/password users who link Google/Apple become hybrid accounts and keep password login enabled.
|
|
|
|
### Login and Signup Surface
|
|
- **D-09:** Show Google and Apple sign-in buttons on both login and signup pages. The provider callback decides whether to create, link, or sign in.
|
|
- **D-10:** If required provider config is missing, render the provider button disabled with a plain not-configured state. This keeps the intended UI visible in local/dev.
|
|
- **D-11:** Google and Apple have equal prominence wherever provider buttons are shown.
|
|
- **D-12:** Successful social sign-in redirects to `/`, matching existing email/password login and signup behavior.
|
|
|
|
### Provider Profile Data
|
|
- **D-13:** Store provider display name and avatar URL where providers supply them, in addition to provider, provider subject, verified email, and timestamps.
|
|
- **D-14:** Apple may provide name only on first authorization. Persist Apple name immediately if present in the callback payload.
|
|
- **D-15:** Accept Apple private relay emails as verified emails when Apple marks them verified.
|
|
- **D-16:** If the same provider subject later returns a new verified email, update `user_identities.email`. Provider subject remains the durable external identity key.
|
|
- **D-17:** When provider email changes, update local `users.email` too.
|
|
|
|
### the agent's Discretion
|
|
- Exact table/column names are planner discretion, but the data model must support provider, subject, verified email, display name, avatar URL, provider timestamps, and unique `(provider, subject)`.
|
|
- Exact copy for provider errors, disabled provider buttons, and duplicate/social-only signup messages is planner discretion, but messages must be clear and avoid leaking unnecessary security detail.
|
|
- Planner must handle `users.email` update conflicts explicitly because `users.email` is unique. If a provider email update collides with another local user, do not silently relink or overwrite another account.
|
|
- Exact route names for account/linked-provider status are planner discretion, but the view should be protected and minimal.
|
|
|
|
</decisions>
|
|
|
|
<canonical_refs>
|
|
## Canonical References
|
|
|
|
**Downstream agents MUST read these before planning or implementing.**
|
|
|
|
### Phase Scope
|
|
- `.planning/PROJECT.md` — v2.0 constraints: Google/Apple are identity providers only; Xtablo owns users and sessions.
|
|
- `.planning/REQUIREMENTS.md` — AUTH-08..AUTH-13 define Phase 8 requirements.
|
|
- `.planning/ROADMAP.md` — Phase 8 goal, success criteria, and user-in-loop schema/account-linking gate.
|
|
- `.planning/research/SUMMARY.md` — Research summary for Google/Apple OIDC and local session integration.
|
|
- `.planning/research/STACK.md` — Provider config, OIDC/JWT verification notes, and recommended stack additions.
|
|
- `.planning/research/PITFALLS.md` — OAuth state/nonce, provider subject, Apple first-login-only profile data, and email verification pitfalls.
|
|
|
|
### Prior Auth Decisions
|
|
- `.planning/phases/02-authentication/02-CONTEXT.md` — Existing session, cookie, CSRF, middleware order, rate-limit, and password-auth decisions.
|
|
- `.planning/phases/07-deploy-v1/07-CONTEXT.md` — Production env-file secret management and deploy constraints relevant to provider secrets.
|
|
|
|
### Codebase Entry Points
|
|
- `backend/internal/web/handlers_auth.go` — Existing signup/login/logout handlers; social callback should reuse local session creation behavior.
|
|
- `backend/internal/auth/session.go` — Existing session store; social sign-in must issue sessions through this path.
|
|
- `backend/internal/auth/cookie.go` — Session cookie behavior.
|
|
- `backend/internal/auth/middleware.go` — `ResolveSession`, `RequireAuth`, `RedirectIfAuthed` middleware behavior.
|
|
- `backend/internal/db/queries/users.sql` — Existing user insert/lookup query shape; Phase 8 changes nullable password handling and identity lookup/linking.
|
|
- `backend/internal/db/queries/sessions.sql` — Existing session query shape.
|
|
- `backend/migrations/0002_auth.sql` — Current `users.password_hash NOT NULL`; Phase 8 migration must change this.
|
|
- `backend/templates/auth_login.templ` — Login page provider button insertion point.
|
|
- `backend/templates/auth_signup.templ` — Signup page provider button insertion point.
|
|
- `backend/.env.example` — Add Google and Apple provider config without secrets.
|
|
|
|
</canonical_refs>
|
|
|
|
<code_context>
|
|
## Existing Code Insights
|
|
|
|
### Reusable Assets
|
|
- `auth.Store.Create` / `auth.Store.Rotate`: issue local session cookies after provider verification.
|
|
- `auth.SetSessionCookie`: keep cookie behavior identical across email/password and social sign-in.
|
|
- `auth.RedirectIfAuthed`: existing auth-page redirect behavior should still apply to login/signup pages.
|
|
- `templates.LoginPage` and `templates.SignupPage`: wrap auth forms in a Card; provider buttons can be added consistently on both pages.
|
|
- `ui.Button`: reusable button component for provider buttons and disabled provider states.
|
|
|
|
### Established Patterns
|
|
- Form handlers read values through `r.PostFormValue` because gorilla/csrf may consume request bodies; OAuth callback handlers should keep callback parsing simple and avoid body-read surprises.
|
|
- Existing successful auth paths redirect to `/`; social sign-in should match this.
|
|
- Password hashing is argon2id and remains only for email/password users.
|
|
- Sessions are DB-backed and store a SHA-256 hash of the raw cookie token; provider tokens must never become Xtablo sessions.
|
|
- Route ordering in chi uses static routes before parametric routes; provider start/callback routes should follow this pattern.
|
|
- Config lives in env vars and `.env.example`; production secrets are host `.env` values per deploy context.
|
|
|
|
### Integration Points
|
|
- `backend/internal/web/router.go`: add provider start/callback routes around existing auth page and POST routes.
|
|
- `backend/cmd/web/main.go`: parse provider config and pass it through auth/web dependencies.
|
|
- `backend/migrations/`: add Phase 8 migration for nullable password hashes and `user_identities`.
|
|
- `backend/internal/db/queries/`: add identity insert/get/update/linking queries, and adjust user queries for nullable password hash.
|
|
- `backend/templates/auth_login.templ` and `backend/templates/auth_signup.templ`: add equal-prominence Google/Apple buttons and disabled config states.
|
|
- `backend/templates/layout.templ` or a new account template: expose a minimal linked providers view for authenticated users.
|
|
|
|
</code_context>
|
|
|
|
<specifics>
|
|
## Specific Ideas
|
|
|
|
- Account linking is intentionally optimistic for verified provider emails to keep the early product flow smooth.
|
|
- Provider subject is the stable identity key; email can change and should not drive relinking once a subject is linked.
|
|
- Apple private relay is accepted as a normal verified email when Apple verifies it.
|
|
- Store display name and avatar URL when supplied, but do not make them required for account creation.
|
|
- Local `users.email` should follow provider email changes, but planner must guard the unique-email conflict path.
|
|
|
|
</specifics>
|
|
|
|
<deferred>
|
|
## Deferred Ideas
|
|
|
|
- Provider unlink actions.
|
|
- Add-password/password management for social-only users.
|
|
- Password reset flow.
|
|
- MFA.
|
|
- Passkeys/WebAuthn.
|
|
- Redirecting back to the originally requested protected URL after OAuth.
|
|
- Provider-specific welcome/onboarding page.
|
|
|
|
</deferred>
|
|
|
|
---
|
|
|
|
*Phase: 8-Social Sign-in*
|
|
*Context gathered: 2026-05-15*
|