# 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*
