# Shared SSO Implementation Plan (Mission Control + Related Apps) ## Summary Implement single sign-on across apps by using one parent domain and one shared session cookie, with Supabase Auth as the identity source and a central auth origin (`auth.topdoglabs.com`). Use one dedicated auth project/service for this logic so auth code is not duplicated across app projects. This gives: - One login across apps - Silent cross-app authentication - Global logout --- ## Chosen Decisions 1. Use custom subdomains under one parent domain. 2. Keep Supabase as the central identity provider. 3. Use silent auto-login between apps. 4. Use global logout across apps. --- ## Target Domain Layout - `auth.topdoglabs.com` -> central auth service - `mission.topdoglabs.com` -> Mission Control - `gantt.topdoglabs.com` -> Gantt Board - (optional) `blog.topdoglabs.com` -> Blog Backup Important: shared-cookie SSO will not work across `*.vercel.app` app URLs. --- ## Auth and Session Architecture 1. Centralize login/logout/session endpoints on `auth.topdoglabs.com`. 2. Keep credentials and user identity in Supabase Auth. 3. Use a shared `sessions` table in Supabase for web sessions. 4. Issue one cookie for all subdomains: - Name: `tdl_sso_session` - `Domain=.topdoglabs.com` - `Path=/` - `HttpOnly` - `Secure` (production) - `SameSite=Lax` 5. Each app middleware: - reads `tdl_sso_session` - validates via auth/session endpoint - redirects to auth login if missing/invalid, with `return_to` URL 6. Logout from any app: - revoke server session - clear shared cookie for `.topdoglabs.com` - signed out everywhere --- ## API Contracts ### `POST /api/sso/login` (auth domain) Input: ```json { "email": "user@example.com", "password": "********", "rememberMe": true } ``` Behavior: - authenticates against Supabase Auth - creates shared session row - sets shared cookie Response: ```json { "success": true, "user": { "...": "..." }, "session": { "expiresAt": "ISO8601", "rememberMe": true } } ``` ### `GET /api/sso/session` (auth domain) Behavior: - reads `tdl_sso_session` - validates against sessions table Response: ```json { "authenticated": true, "user": { "...": "..." } } ``` or ```json { "authenticated": false } ``` ### `POST /api/sso/logout` (auth domain) Behavior: - revokes current session in DB - clears shared cookie on `.topdoglabs.com` Response: ```json { "success": true } ``` ### `GET /api/sso/authorize?return_to=` (auth domain) Behavior: - if authenticated: redirect (302) to `return_to` - if unauthenticated: redirect to login - validate `return_to` against allowlist --- ## Data Model (Supabase) Use/standardize `sessions` table: - `user_id` (uuid, fk) - `token_hash` (text, unique) - `created_at` (timestamptz) - `expires_at` (timestamptz) - `revoked_at` (timestamptz, nullable) Recommended optional fields: - `last_seen_at` - `ip` - `user_agent` - `remember_me` Indexes: - `token_hash` - `user_id` - `expires_at` Security: - store only SHA-256 hash of token - never store raw session token --- ## Vercel Subdomain Setup (Step-by-Step) 1. In each Vercel project, open `Settings -> Domains`. 2. Add the desired subdomain for that specific project: - Mission Control project -> `mission.topdoglabs.com` - Gantt project -> `gantt.topdoglabs.com` - Auth project -> `auth.topdoglabs.com` 3. If domain DNS is managed by Vercel nameservers, Vercel usually auto-creates the needed records. 4. If DNS is external: - create the exact DNS record shown by Vercel on the domain card - subdomains are typically `CNAME` - apex/root is typically `A` 5. Wait for each to show verified/valid in Vercel. 6. Keep the `*.vercel.app` URLs for testing, but use custom subdomains for production SSO. --- ## Subdomain Knowledge (Quick Primer) ### What a subdomain is - A subdomain is a child host under a parent domain. - Example: - parent/root domain: `topdoglabs.com` - subdomains: `mission.topdoglabs.com`, `gantt.topdoglabs.com`, `auth.topdoglabs.com` ### Why this matters for SSO - Browsers only allow one site to share a cookie with another site when both are under the same parent domain and the cookie `Domain` is set to that parent. - For your case: set cookie `Domain=.topdoglabs.com` so all `*.topdoglabs.com` apps can read/send it. - `mission-control.twisteddevices.com` and `gantt-board.twisteddevices.com` cannot share a parent-domain cookie with each other. ### DNS records you will see - Subdomain to app mapping is commonly a `CNAME` record. - Apex/root domain usually uses `A`/`ALIAS`/`ANAME` depending on DNS provider. - In Vercel, always follow the exact record values shown in each project’s Domains panel. ### SSL/TLS on subdomains - Vercel provisions certificates for added custom domains/subdomains after verification. - SSO cookies with `Secure` require HTTPS in production, so certificate readiness is required before final auth testing. ### Cookie scope rules (important) - `Domain=.topdoglabs.com`: cookie is sent to all subdomains under `topdoglabs.com`. - `Domain=mission.topdoglabs.com` or no `Domain` set: cookie is host-only, not shared with other subdomains. - `SameSite=Lax`: good default for first-party web app navigation between your subdomains. ### Recommended host roles - `auth.topdoglabs.com`: login, logout, session validation endpoints - `mission.topdoglabs.com`: Mission Control app - `gantt.topdoglabs.com`: Gantt Board app - Optional static/marketing hosts can stay separate, but app surfaces participating in SSO should remain under `*.topdoglabs.com`. --- ## Subdomain Rollout Checklist 1. Add `mission.topdoglabs.com` to Mission Control Vercel project. 2. Add `gantt.topdoglabs.com` to Gantt project. 3. Add `auth.topdoglabs.com` to auth project (or auth routes host). 4. Verify each domain shows valid configuration and active HTTPS. 5. Update environment variables: - `AUTH_ORIGIN=https://auth.topdoglabs.com` - `COOKIE_DOMAIN=.topdoglabs.com` 6. Deploy auth cookie changes with `Domain=.topdoglabs.com`. 7. Test login on one app, then open the other app in a new tab. 8. Test global logout propagation across both apps. --- ## Common Subdomain Troubleshooting 1. Symptom: still getting separate logins. - Check cookie `Domain` is `.topdoglabs.com`, not host-only. 2. Symptom: login works on one app but not another. - Confirm both apps are truly on `*.topdoglabs.com`, not `*.vercel.app`. 3. Symptom: cookie not set in production. - Confirm HTTPS is active and cookie has `Secure`. 4. Symptom: redirect loops. - Check `return_to` allowlist and ensure auth/session endpoint trusts both app origins. 5. Symptom: logout not global. - Ensure logout revokes DB session and clears cookie with same name + same domain/path attributes. --- ## Reusable Pattern for Future Websites Use this as the standard for any new web app that should join shared SSO. ### Onboarding standard for a new app 1. Put the app on a subdomain under `*.topdoglabs.com` (for example `newapp.topdoglabs.com`). 2. Add middleware/route guard that checks `tdl_sso_session`. 3. If no valid session, redirect to `https://auth.topdoglabs.com/login?return_to=`. 4. On sign-out, call central logout endpoint (`POST /api/sso/logout`) instead of local-only logout. 5. Ensure app URLs are added to auth `return_to` allowlist. ### Required environment variables (per app) - `AUTH_ORIGIN=https://auth.topdoglabs.com` - `COOKIE_NAME=tdl_sso_session` - `COOKIE_DOMAIN=.topdoglabs.com` ### Required auth behavior (per app) - Do not create app-specific login cookie names for SSO surfaces. - Do not set host-only session cookies for authenticated app pages. - Treat auth service as source of truth for session validity. ### Minimal integration contract - App must be able to: - redirect unauthenticated users to auth origin with `return_to` - accept authenticated return and continue to requested page - trigger global logout - handle expired/revoked session as immediate signed-out state ### Naming convention recommendation - Domain pattern: `.topdoglabs.com` - Session cookie: `tdl_sso_session` - Auth host: `auth.topdoglabs.com` This keeps every future app consistent and avoids one-off auth logic. --- ## Migration Plan 1. Move apps to custom subdomains. 2. Introduce shared cookie `tdl_sso_session`. 3. Update auth cookie set/clear to include `Domain=.topdoglabs.com`. 4. Centralize auth endpoints on `auth.topdoglabs.com`. 5. Update app middleware to redirect to auth domain with `return_to`. 6. Keep legacy app-local cookie compatibility for 7-14 days. 7. Remove legacy cookie logic after rollout. --- ## Security Controls 1. Strict allowlist for `return_to` URLs (prevent open redirects). 2. CSRF protection on login/logout POST endpoints. 3. Session TTL: - short session: 12 hours - remember me: 30 days 4. Treat expired/revoked sessions as immediate logout. 5. Rotate session token on sensitive account actions. --- ## Test Cases 1. Login once, open app A then app B, no second login prompt. 2. Deep-link directly into app B while logged in from app A. 3. Logout in app A, refresh app B, user is logged out. 4. Expired cookie redirects to auth login. 5. Revoked session is denied in all apps. 6. Invalid `return_to` is rejected and replaced by safe default. 7. Remember-me survives browser restart; non-remember does not. 8. Cookie flags validated in production (`Secure`, `HttpOnly`, domain-scoped). --- ## Assumptions 1. Both apps remain in the same Supabase project/database. 2. Both apps can be served under `*.topdoglabs.com`. 3. Global logout means current browser session is invalidated across all apps. 4. Existing `mission_control_session` is replaced by `tdl_sso_session`.