Kizaki
Guides

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 principal block 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.

  1. enable the providers you want in auth
  2. store app-specific profile data in your own entities
  3. 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:

  • __User for the built-in authenticated identity
  • one or more app entities such as Profile or OrganizationMembership
  • a principal projection 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

  • __User stays focused on identity
  • your schema owns business-specific profile data
  • policies and server code can rely on principal without extra joins everywhere

For a new app:

  1. start with email
  2. add Google, GitHub, or Apple when the product needs them
  3. 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.

On this page