mission-control/docs/sso-implementation-plan.md

9.5 KiB
Raw Blame History

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:

{ "email": "user@example.com", "password": "********", "rememberMe": true }

Behavior:

  • authenticates against Supabase Auth
  • creates shared session row
  • sets shared cookie

Response:

{ "success": true, "user": { "...": "..." }, "session": { "expiresAt": "ISO8601", "rememberMe": true } }

GET /api/sso/session (auth domain)

Behavior:

  • reads tdl_sso_session
  • validates against sessions table

Response:

{ "authenticated": true, "user": { "...": "..." } }

or

{ "authenticated": false }

POST /api/sso/logout (auth domain)

Behavior:

  • revokes current session in DB
  • clears shared cookie on .topdoglabs.com

Response:

{ "success": true }

GET /api/sso/authorize?return_to=<url> (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-rho-pink.vercel.app and gantt-board.vercel.app 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 projects 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.
  • 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.
  • 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=<current_url>.
  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: <app>.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.