# Phase 8: Social Sign-in - Context **Gathered:** 2026-05-15 **Status:** Ready for planning ## 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. ## 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. ## 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. ## 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. ## 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. ## 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. --- *Phase: 8-Social Sign-in* *Context gathered: 2026-05-15*