Crossdeck University
Watch — identify() in your real auth listener, the right way Film in production
0:00 / 0:00
Lesson 3 of 3 · Cross-platform identity

Wire your auth provider

You've seen identify(). This is exactly where it goes for your auth provider — Firebase, Supabase, Auth0, Clerk, NextAuth — and the one timing rule that makes it reliable.

5 min Web

When you're done: every signed-in user is identified, the People page shows them by name, and their pre-signup timeline is still attached.

1 What this is & why it matters

"Who is signed in" depends entirely on your provider

The SDK auto-captures sessions, page views, clicks, and errors the moment you call init() — but every event is attached to an anonymous device ID. To turn that anonymous timeline into a named user, you call Crossdeck.identify(userId) with the same stable ID your auth provider already gives you.

Why isn't this automatic? Because "who is signed in" isn't knowable from inside a generic browser SDK — the shape of "the current user" depends on which provider you wired. Firebase exposes auth.currentUser.uid; Supabase exposes session.user.id; Auth0 exposes user.sub; Clerk exposes user.id. So you point Crossdeck at the right one. It's one line per provider.

2 Wire it up by provider

Identify on sign-in, reset on sign-out

Find your provider, drop this into your auth state listener, and you're done. The pattern is always the same: identify(userId) when a user is present, reset() when they sign out.

import { onAuthStateChanged } from "firebase/auth";

onAuthStateChanged(auth, (user) => {
  if (user) Crossdeck.identify(user.uid);
  else Crossdeck.reset();
});
3 How it works, piece by piece

The one timing rule — and what the alias preserves

The rule that makes every one of these reliable: call identify() when the user resolves, not at init(). Auth providers hydrate asynchronously — Firebase's onAuthStateChanged can fire 50–800ms after init; Auth0's user is undefined during the redirect handoff; Clerk and NextAuth gate on a loaded/status flag. That's exactly why the call lives inside the listener or effect: it runs the moment the real user ID exists, never against a half-loaded null.

And here's what you get for free: the alias preserves the anonymous timeline. When you call identify(), Crossdeck doesn't start a fresh person — it aliases the anonymous device ID into your user, so everything that happened before sign-up stays attached to them. Your sign-up funnel survives, because the visitor who browsed pricing and the user who converted are one record, not two.

4 The green result in your dashboard

Signed-in users, by name — history intact

Sign in. Within a moment the People page shows that user by their real ID, with the pre-signup events still on their timeline. Sign out and the next session is anonymous again — clean, no leakage between accounts on a shared device.

app.cross-deck.com · people
identified · timeline intact

user_847 on the People page — named, with the pricing-page visit from before they signed up still attached. One person, whole story.

One line per provider, inside your auth state listener: identify(userId) on sign-in, reset() on sign-out. The timing rule is the whole trick — call it when the user resolves, never at init against a half-loaded null. The alias keeps the pre-signup timeline attached. That's Cross-platform identity, done.