Why We Built Our Own Cross-Domain SSO (And When You Should Too)

A deep dive into Supabase cross-domain authentication: why we built a custom SSO system with Lovable instead of using Clerk or Auth0, the token exchange architecture, and a complete security testing checklist.

Chris

Chris

January 23, 2026 · 16 min read

Why We Built Our Own Cross-Domain SSO (And When You Should Too)
A hub-and-satellite architecture enables seamless cross-domain authentication

When you run multiple apps under one brand — a marketing site, a customer portal, an internal tool — authentication becomes a headache. Users expect to log in once and move seamlessly between apps. But implementing that "single sign-on" experience? That's where it gets complicated.

We faced this exact challenge with our satellite app architecture. After evaluating the major players — Clerk, Auth0, Firebase — we decided to build our own SSO system on top of Supabase. Here's why, how we built it entirely with Lovable, and more importantly, when you should (and shouldn't) follow the same path.

Lovable

Built Entirely with Lovable

This entire SSO system — the Edge Functions, React hooks, database schema, and both the hub and satellite apps — was built using Lovable. What would typically take weeks of careful security engineering was completed in days through iterative prompting and Lovable's native Supabase integration.

The Challenge: localStorage is Domain-Specific

Here's the fundamental problem: Supabase stores sessions in localStorage, which is domain-specific. A session on dot2.solutions simply won't be available on emailsign.dot2.solutions by default.

This is a browser security feature, not a bug. But it means that if you want users to move between subdomains without re-authenticating, you need to build something custom.

Our requirements were clear:

  • Single login: Users authenticate once on the hub
  • Seamless navigation: Moving to a satellite app doesn't require re-authentication
  • Federated logout: Logging out anywhere logs you out everywhere
  • Security: No token leakage, no replay attacks, strict domain validation
  • Support all auth methods: Email/password, Google, future providers

We also had a constraint: we were already using Supabase for our database and Row Level Security (RLS) policies. Any auth solution needed to integrate tightly with that — and we wanted to build the entire thing with Lovable.

SSO Architecture Options We Considered

Before diving into implementation, we mapped out the three main approaches for cross-domain authentication:

Option 1: Central Auth Hub with Token Exchange (Our Choice)

This is the cleanest approach for satellite app architectures. Here's the flow:

  1. Satellite app (emailsign) redirects to dot2.solutions/login?returnTo=https://emailsign.dot2.solutions/auth/callback
  2. User authenticates on the hub using any method (email/password, Google, etc.)
  3. Hub generates a short-lived, single-use token and redirects back
  4. Satellite exchanges the token for a full Supabase session

Pros: Supports all auth methods, minimal code on satellites, leverages existing login UI, secure token exchange

Cons: Requires building the token exchange layer

Option 2: Shared Cookie Domain (True SSO)

Configure Supabase to use cookies scoped to .dot2.solutions instead of localStorage:

// Both projects would use this config
export const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
  auth: {
    storage: cookieStorage,  // Custom storage using cookies
    storageKey: 'sb-session',
    flowType: 'pkce',
  }
});

Pros: True SSO — login once, authenticated everywhere instantly

Cons: Complex custom cookie handling, potential CORS issues, working against how Supabase was designed, Safari/incognito restrictions

Option 3: Magic Link / OTP for Subdomain

Generate a one-time token on the main domain that the subdomain can exchange for a session via Supabase's built-in OTP verification.

Pros: Uses Supabase's native auth flows

Cons: Still requires custom token generation, less control over expiry and validation

Why We Chose Option 1

Token Exchange gives you 90% of the SSO benefit with significantly less complexity and risk. Cookie SSO is elegant in theory, but you'd be working against Supabase's grain. Unless you're spinning up many subdomains where users constantly hop between them, the redirect approach is the right choice.

For our use case — emailsign as a lead generation tool — Option 1 was ideal because:

  • Supports ALL auth methods: email/password, Google, future providers
  • Handles login AND signup: Same flow for both
  • Minimal code on satellite: Just redirect buttons, no forms
  • Uses existing UI: Leverages our polished login/signup on dot2.solutions
  • Secure: Server-validated tokens, not raw JWTs in URLs
  • Clear user journey: Users see dot2.solutions branding during auth

The Managed Alternatives We Evaluated

Before building, we seriously considered the major authentication platforms:

Solution Best For Pricing Pros Cons
Clerk Modern SaaS startups $25/mo + $0.02/MAU Excellent DX, pre-built UI components, React-first Vendor lock-in, separate user store, limited backend integration
Auth0 Enterprise with compliance needs $30k+ for cross-domain SSO HIPAA/SOC2 ready, SAML/OIDC support, mature platform Complex configuration, expensive at scale, overkill for smaller teams
Firebase Auth Google ecosystem projects Free to 50k MAU Simple setup, generous free tier, good mobile support No native organization support, weak cross-domain story, separate from your DB
Custom Supabase Teams already on Supabase needing full control Free (your infrastructure) Full control, native RLS integration, zero licensing fees Manual implementation, you own the security surface

Why Not Clerk?

Clerk's developer experience is genuinely excellent — it's built for modern React apps and works well with Lovable-generated code. Their React components are beautiful, and the setup is almost trivial. But for our use case, there were blockers:

  • Separate user store: Clerk manages its own user database. We needed users in Supabase for RLS policies to work.
  • Migration required: We'd need to migrate from Supabase Auth to Clerk for authentication while keeping Supabase for the database — a common pattern, but added complexity.
  • Cross-domain limitations: Clerk's multi-domain setup requires enterprise pricing and still doesn't give you the control we needed.
  • Vendor dependency: Auth is the most critical part of any app. We weren't comfortable with that much lock-in.

Why Not Auth0?

Auth0 is the gold standard for enterprise authentication. Universal Login means one login page for all your apps, and sessions work across subdomains automatically. But:

  • Cost: Cross-domain SSO (organizations feature) starts at enterprise pricing — $30k+ annually.
  • Complexity: The configuration surface is massive. For a small team, it's overkill.
  • Still separate from Supabase: You'd need to sync users between Auth0 and Supabase, adding complexity.

Why Not Firebase Auth?

Firebase Auth is free and simple (7,500 MAU free tier), but:

  • No native cross-domain support: You'd still need to build the token exchange layer yourself.
  • Separate from your DB: Same sync problem as Auth0.
  • Google ecosystem lock-in: If you're not on Firebase for everything else, it's a weird fit.

Why We Chose Custom Supabase + Lovable

Given our constraints, building on Supabase with Lovable made sense:

  1. Already using Supabase: Our users were already in auth.users. No sync needed.
  2. RLS integration: Auth claims flow directly into Postgres policies. No middleware required.
  3. Edge Functions: We could build the token exchange logic as serverless functions, deployed alongside our app.
  4. Cost: Zero additional licensing. Just our existing Supabase subscription.
  5. Full control: We own the security surface. We can audit every line of code.
  6. Lovable's native Supabase support: Edge Functions, database migrations, and types all sync automatically.

The trade-off? We had to build it ourselves. But with Lovable, the implementation was surprisingly fast.

Building the SSO System with Lovable

Here's what made Lovable ideal for this project:

🚀 Edge Functions

Lovable's native Supabase integration meant we could describe the token generation and exchange logic, and Lovable generated properly typed Edge Functions that deployed automatically.

🗄️ Database Schema

The sso_tokens table with proper indexes, RLS policies, and expiry logic was created through Lovable's migration system with a single prompt.

🔐 Security Reviews

Lovable helped identify edge cases — React StrictMode double-mounts, token replay attacks, domain validation — and implemented fixes iteratively.

📱 Multi-App Consistency

Both the hub (dot2.solutions) and satellite app (emailsign) were built in Lovable, making it easy to keep the useSSO hook in sync across projects.

What would typically take weeks of careful security engineering was completed in days through iterative prompting. Each security concern — token expiry, replay protection, domain whitelisting — was addressed as we built.

Simple vs. Robust: The Implementation Decision

Early on, we considered a simpler approach: just pass Supabase tokens in the URL fragment after auth and let the satellite pick them up. No database, no Edge Functions.

Aspect Simple (Fragment) Robust (Token Exchange)
Security Tokens in URL fragment (client-only) One-time codes, server-validated
Complexity 0 edge functions, 0 tables 2 edge functions, 1 table
Audit Trail None Full logging of SSO flows
Token Size Full JWT (~800 chars) UUID (~36 chars)
Failure Points Fewer More (but explicit)

For a single satellite app as a lead generator, the simpler approach would work. But since we anticipated adding more tools under the dot2.solutions umbrella — proposal generators, invoice tools, signature builders — building proper infrastructure now saves pain later.

With Lovable, the "robust" approach wasn't significantly more effort anyway. The added complexity was handled through prompting, not manual coding.

🤔 Not Sure Which Approach Fits Your Stack?

Every team's requirements are different — existing auth provider, number of satellite apps, compliance needs, and engineering capacity all factor in. We can help you evaluate the tradeoffs and pick the right path.

The Architecture: Hub and Satellite

Our SSO system uses a hub-and-satellite model. The diagram below shows the complete token exchange flow:

┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│   Satellite     │────▶│    Auth Hub     │────▶│    Supabase     │
│   (emailsign)   │◀────│  (dot2.solutions)│◀────│   (Backend)     │
└─────────────────┘     └─────────────────┘     └─────────────────┘

How It Works

Try the interactive demo below to see each step of the SSO flow in action:

  1. User visits satellite app (e.g., emailsign.dot2.solutions)
  2. Satellite detects no session → Redirects to hub with returnTo parameter
  3. Hub authenticates user (or uses existing session)
  4. Hub generates short-lived SSO token → Stores in sso_tokens table
  5. Hub redirects to satellite callback with token
  6. Satellite exchanges token → Gets full Supabase session
  7. Token is invalidated → Prevents replay attacks

Key Security Features

  • 5-minute token expiry: Tokens are only valid briefly
  • Single-use tokens: Once exchanged, the token is marked as used
  • Domain whitelist: Only approved domains can receive tokens
  • Cryptographic tokens: Generated using crypto.randomUUID()
  • Server validation: Satellites can't just accept any token — it must be verified against the database

Implementation Highlights

Edge Functions

Two Supabase Edge Functions power the system:

generate-sso-token: Called by the hub after authentication. Validates the returnTo URL against the allowlist, generates a secure token, and stores it in the database.

exchange-sso-token: Called by the satellite on callback. Validates the token, checks expiry, creates a Supabase session using the token_hash flow, and marks the token as used.

React Hooks

The useSSO hook provides a clean API:

const { 
  generateSSOToken,    // Hub: create token for satellite
  exchangeSSOToken,    // Satellite: exchange token for session
  buildSSOLoginUrl,    // Satellite: redirect to hub login
  buildSSOLogoutUrl,   // Both: federated logout
  checkCentralSession  // Satellite: validate hub session still active
} = useSSO();

Session Synchronization

The useSSOSessionSync hook ensures satellites stay in sync with the hub:

useSSOSessionSync(
  () => {
    // Called when central session expires
    signOut();
    navigate('/');
  },
  60000, // Check every 60 seconds
  true   // Enabled
);

Because both hub and satellites share the same Supabase project, when the hub's session is invalidated (user signs out), the satellite's refreshSession() call fails — triggering automatic logout.

React StrictMode Protection

A subtle bug we hit: React's StrictMode double-mounts components in development, causing the token exchange to fire twice. The second call fails because the token is already used.

The fix: a useRef guard in the callback component:

const hasExchanged = useRef(false);

useEffect(() => {
  if (hasExchanged.current) return;
  hasExchanged.current = true;
  
  exchangeToken();
}, []);

This was exactly the kind of edge case that iterative development with Lovable caught quickly — test, see the error, prompt for a fix, deploy.

Testing Checklist

Before deploying your SSO system, verify each of these scenarios:

🔐 Security Tests

  • Token replay protection: Can a token be used twice? (Should fail)
  • Token expiry: Does a 6-minute-old token get rejected?
  • Domain validation: Does an unauthorized returnTo URL get blocked?
  • Invalid token handling: Does a malformed token fail gracefully?

🔄 Flow Tests

  • Fresh login: User with no session → hub login → satellite callback → session created
  • Existing session: User already logged into hub → satellite redirects → instant callback
  • Federated logout: Logout on hub → satellite session invalidated within sync interval
  • Logout on satellite: Redirects to hub logout → returns to satellite logged out

🧪 Edge Cases

  • React StrictMode: Does double-mount cause issues? (useRef guard)
  • Safari/incognito: Do third-party cookie restrictions break the flow?
  • Slow networks: Does the 5-minute window handle latency?
  • Token cleanup: Are expired tokens being purged from the database?

When NOT to Use This Approach

Our custom SSO solution isn't right for everyone. Choose a managed solution if:

  • You need SAML/OIDC enterprise SSO: Integrating with Okta, Azure AD, or Google Workspace? Use Auth0 or WorkOS.
  • You need compliance certifications fast: SOC2, HIPAA, PCI-DSS? Managed providers have these ready.
  • You're not on Supabase: The tight integration is the main benefit. Without it, you're just building auth from scratch.
  • Your team lacks security expertise: Auth is a security-critical surface. If you can't audit the code, use a managed solution.
  • You need many subdomains immediately: If you're planning 5+ satellite apps where users hop constantly, Cookie SSO or a managed solution might be worth the complexity.

Conclusion

Building our own SSO on Supabase — using Lovable for the entire implementation — gave us exactly what we needed: seamless cross-domain authentication that integrates natively with our database security model, with zero additional licensing costs.

The implementation is straightforward — two Edge Functions, a React hook, and careful attention to security details. With Lovable's iterative development approach, what seemed like a complex security project became manageable: prompt, test, refine, deploy.

For teams already on Supabase who need cross-domain auth, it's a compelling option. Just remember: you're taking on responsibility for a critical security surface. Test thoroughly, audit regularly, and know when to reach for a managed solution instead.

📊 The Result

This SSO system now powers authentication for emailsign.dot2.solutions with zero reported authentication issues since launch. Token exchange completes in under 200ms, and federated logout propagates across all satellite apps within seconds.

Free email signature generator app powered by dot2.solutions SSO
emailsign.dot2.solutions — a free email signature generator built with Lovable, powered by our SSO architecture

Want the Full Implementation?

Get the complete source code for the Edge Functions, React hooks, and callback components in our Developer Lab. Copy, paste, and customize for your own satellite apps.

View SSO Satellite Setup Guide →

Need Help With Your Authentication Architecture?

At dot2.solutions, we help teams design and implement authentication systems that scale — whether that's custom Supabase SSO, Clerk integration, or enterprise Auth0 deployments. We build with Lovable to ship faster without compromising on security.

💬 Start a chat with us →

SSOAuthenticationSupabaseSecurityArchitectureTypeScriptLovable

Questions fréquentes

D'autres questions ?

Contactez-nous

Share this article

Building on Lovable? Let's Talk.

As a top-ranked Lovable expert, we help teams ship production-grade web apps — SEO, GEO, and SSR done right from day one.

No commitment required • Free 30-minute consultation • Expert guidance