Authentication
Set up users, sessions, and principal fields for apps that need a stable identity model.
Use this guide when your app needs authenticated users and application-specific identity fields such as display name, organization, or plan state.
The important distinction is:
- authentication tells the platform who the user is
- the
principalblock tells the rest of your app which identity fields are available everywhere else
That second part is what makes the auth model practical. Your policies and server functions should not need to rediscover the same joins on every request.
Recommended Setup
- enable the providers you want in
auth - store app-specific profile data in your own entities
- project only the fields you need into
principal
auth {
providers: [email, google],
sessionDuration: 30d,
principal {
displayName: string @from(Profile.displayName),
organizationId: Organization.id? @from(Profile.organizationId),
}
}Model Identity In Layers
The recommended data model is:
__Userfor the built-in authenticated identity- one or more app entities such as
ProfileorOrganizationMembership - a
principalprojection that exposes the few fields the rest of your app actually needs
For example, if your app is multi-tenant, you probably want:
- a user-facing
displayName - a current
organizationId - maybe a current role or plan-related attribute
That is enough for policies and UI state without bloating the principal model.
Why This Pattern Works
__Userstays focused on identity- your schema owns business-specific profile data
- policies and server code can rely on
principalwithout extra joins everywhere
Recommended Provider Strategy
For a new app:
- start with
email - add Google, GitHub, or Apple when the product needs them
- keep the first version of the principal small
You can always add more providers later, but a small principal is a long-term advantage because it keeps authorization logic understandable.
How This Changes Application Code
Once auth is configured well, the rest of your stack becomes simpler:
- your server functions use
getPrincipal() - your policies compare against
principal.* - your browser app relies on the session being established rather than manually threading identity everywhere
That is the real payoff of doing auth in the schema instead of wiring it ad hoc in route handlers.