Identity and authentication
Lakebed Auth is the built-in identity layer for capsules. Guest auth works with zero setup, and Google sign-in is first-party: no OAuth dashboards, no keys to configure, no redirect URIs to register. Render <SignInWithGoogle /> and it works, in dev and in hosted deploys.
Identity contract
An authenticated identity has one durable authorization key:
ctx.auth.userId === ctx.auth.subject; // google:usr_...
userId is opaque and immutable. It does not encode the deploy hostname, so a generated deploy URL and a custom domain resolve to the same userId. Key all user-owned data on it.
ctx.auth.identityAliases lists audience-scoped legacy aliases (google:ps_...), including at least the current audience's alias even for apps that never changed hostnames. Aliases exist only so an app with rows keyed before stable subjects existed can find and rewrite them. Never key new data on an alias; always use userId.
Email, emailVerified, name, and picture are profile data. A verified email may help your app locate a pending invitation, but it is never an authorization key and never evidence of access — not even as a fallback while verification is unavailable. Email changes do not change userId, and two accounts with the same email remain distinct users.
What your app can rely on
- Tokens are bound to their origin. A token issued for one hostname cannot be replayed at another.
- Sign-in state distinguishes a bad token from an unreachable verifier. Public capsules fall back to an unauthenticated guest when verification fails or is unavailable; an outage does not erase the browser's stored session, so authentication recovers on its own when service returns.
- Revoking access takes effect immediately for new requests and live WebSocket subscriptions. Signing out clears private query caches in the browser.
- Signing in again after revocation restores the same
userId, but previously issued tokens stay dead. - Deleting an account invalidates it: subsequent verification of its tokens fails.
- When an app requests profile fields, the user approves first. Unapproved profile data is never exposed to the app.
Auth shape
type Auth = {
userId: string;
subject?: string; // same immutable key as userId for authenticated identities
identityAliases?: string[]; // migration-only audience-scoped aliases
displayName: string;
provider: "guest" | "google";
isGuest: boolean;
isAuthenticated: boolean;
isLoading?: boolean; // client-only
email?: string;
emailVerified?: boolean;
picture?: string;
};
Local dev
npx lakebed dev uses first-party Lakebed Auth automatically, including real Google sign-in on localhost. Nothing to configure.
For guest identities in dev, set the global guest with npx lakebed auth as alice, or use per-tab identities with ?lakebed_guest=alice in the app URL.
Migrating from older identities
Identity records created by the previous external broker are not automatically linked to first-party google:usr_... identities; users must sign in again. If application data is keyed by an older provider subject, migrate it through explicit reauthorization or account linking. Never infer a link from email. identityAliases does not cover external-broker subjects; it only lists Lakebed Auth pairwise aliases.