9.5 KiB
9.5 KiB
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
- Use custom subdomains under one parent domain.
- Keep Supabase as the central identity provider.
- Use silent auto-login between apps.
- Use global logout across apps.
Target Domain Layout
auth.topdoglabs.com-> central auth servicemission.topdoglabs.com-> Mission Controlgantt.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
- Centralize login/logout/session endpoints on
auth.topdoglabs.com. - Keep credentials and user identity in Supabase Auth.
- Use a shared
sessionstable in Supabase for web sessions. - Issue one cookie for all subdomains:
- Name:
tdl_sso_session Domain=.topdoglabs.comPath=/HttpOnlySecure(production)SameSite=Lax
- Name:
- Each app middleware:
- reads
tdl_sso_session - validates via auth/session endpoint
- redirects to auth login if missing/invalid, with
return_toURL
- reads
- 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_toagainst 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_atipuser_agentremember_me
Indexes:
token_hashuser_idexpires_at
Security:
- store only SHA-256 hash of token
- never store raw session token
Vercel Subdomain Setup (Step-by-Step)
- In each Vercel project, open
Settings -> Domains. - Add the desired subdomain for that specific project:
- Mission Control project ->
mission.topdoglabs.com - Gantt project ->
gantt.topdoglabs.com - Auth project ->
auth.topdoglabs.com
- Mission Control project ->
- If domain DNS is managed by Vercel nameservers, Vercel usually auto-creates the needed records.
- 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
- Wait for each to show verified/valid in Vercel.
- Keep the
*.vercel.appURLs 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
- parent/root domain:
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
Domainis set to that parent. - For your case: set cookie
Domain=.topdoglabs.comso all*.topdoglabs.comapps can read/send it. mission-control-rho-pink.vercel.appandgantt-board.vercel.appcannot share a parent-domain cookie with each other.
DNS records you will see
- Subdomain to app mapping is commonly a
CNAMErecord. - Apex/root domain usually uses
A/ALIAS/ANAMEdepending 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
Securerequire 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 undertopdoglabs.com.Domain=mission.topdoglabs.comor noDomainset: 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 endpointsmission.topdoglabs.com: Mission Control appgantt.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
- Add
mission.topdoglabs.comto Mission Control Vercel project. - Add
gantt.topdoglabs.comto Gantt project. - Add
auth.topdoglabs.comto auth project (or auth routes host). - Verify each domain shows valid configuration and active HTTPS.
- Update environment variables:
AUTH_ORIGIN=https://auth.topdoglabs.comCOOKIE_DOMAIN=.topdoglabs.com
- Deploy auth cookie changes with
Domain=.topdoglabs.com. - Test login on one app, then open the other app in a new tab.
- Test global logout propagation across both apps.
Common Subdomain Troubleshooting
- Symptom: still getting separate logins.
- Check cookie
Domainis.topdoglabs.com, not host-only.
- Check cookie
- Symptom: login works on one app but not another.
- Confirm both apps are truly on
*.topdoglabs.com, not*.vercel.app.
- Confirm both apps are truly on
- Symptom: cookie not set in production.
- Confirm HTTPS is active and cookie has
Secure.
- Confirm HTTPS is active and cookie has
- Symptom: redirect loops.
- Check
return_toallowlist and ensure auth/session endpoint trusts both app origins.
- Check
- 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
- Put the app on a subdomain under
*.topdoglabs.com(for examplenewapp.topdoglabs.com). - Add middleware/route guard that checks
tdl_sso_session. - If no valid session, redirect to
https://auth.topdoglabs.com/login?return_to=<current_url>. - On sign-out, call central logout endpoint (
POST /api/sso/logout) instead of local-only logout. - Ensure app URLs are added to auth
return_toallowlist.
Required environment variables (per app)
AUTH_ORIGIN=https://auth.topdoglabs.comCOOKIE_NAME=tdl_sso_sessionCOOKIE_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
- redirect unauthenticated users to auth origin with
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
- Move apps to custom subdomains.
- Introduce shared cookie
tdl_sso_session. - Update auth cookie set/clear to include
Domain=.topdoglabs.com. - Centralize auth endpoints on
auth.topdoglabs.com. - Update app middleware to redirect to auth domain with
return_to. - Keep legacy app-local cookie compatibility for 7-14 days.
- Remove legacy cookie logic after rollout.
Security Controls
- Strict allowlist for
return_toURLs (prevent open redirects). - CSRF protection on login/logout POST endpoints.
- Session TTL:
- short session: 12 hours
- remember me: 30 days
- Treat expired/revoked sessions as immediate logout.
- Rotate session token on sensitive account actions.
Test Cases
- Login once, open app A then app B, no second login prompt.
- Deep-link directly into app B while logged in from app A.
- Logout in app A, refresh app B, user is logged out.
- Expired cookie redirects to auth login.
- Revoked session is denied in all apps.
- Invalid
return_tois rejected and replaced by safe default. - Remember-me survives browser restart; non-remember does not.
- Cookie flags validated in production (
Secure,HttpOnly, domain-scoped).
Assumptions
- Both apps remain in the same Supabase project/database.
- Both apps can be served under
*.topdoglabs.com. - Global logout means current browser session is invalidated across all apps.
- Existing
mission_control_sessionis replaced bytdl_sso_session.