can() on. It carries an authenticated actorUserId and, optionally, a view-as state for admin preview. Sessions are the bridge between your identity provider (Clerk, Auth.js, or any other) and Alfiz’s permission evaluation. Alfiz does not issue or validate tokens; you verify the token through your provider and pass the resulting user ID to createSession.
Creating a session
SessionOptions
string
required
The ID of the authenticated user, as issued by your identity provider. This is the user whose real permissions are checked. It is never replaced by a view-as state for access decisions.
ViewAsState | null
Activates a preview session. Pass
null (or omit) for a normal session. Activating a preview is gated for you: when viewAs is non-null, createSession calls assertCanViewAs and throws AccessDeniedError unless the actor holds alfiz_internal.access.view_as. A catalog built with includeAlfizInternal: false declares no such key, so previews fail closed rather than erroring.Return value
AlfizSession<K, P, S>
A session object with
can, canAny, require, and requireAny methods. All checks are preview-narrowed: a view-as session can only see access that both the actor and the previewed subject hold. A preview can narrow what is shown; it can never escalate privileges.Session methods
Always use
actorUserId for audit attribution. It is never the previewed subject. Use subjectUserId for data-scoped surfaces (e.g., “show documents owned by this user”), and it returns the previewed user’s ID during a user preview, and the actor’s ID otherwise.session.snapshot(): the render path under view-as
The async session methods are fine for a gate, but a render path cannot await
inside .map(). session.snapshot() is the session-shaped version of the
per-request snapshot: one fetch per identity, then
synchronous, preview-narrowed checks.
options, meaning pre-resolved scopes and fresh, applies to both identities at
once, so the actor and the preview are always read at the same instant.
A role preview evaluates synchronously with no extra resolution, because role
patterns are global-scope by construction. An unknown role id fails closed.
SessionSnapshotOf<typeof catalog>,
which completes the derived-type family alongside ClientOf, SnapshotOf, and
SessionOf.
Derived session type
UseSessionOf<typeof catalog> to type session variables without writing out the K, P, and S type parameters by hand:
Wiring to Clerk (Next.js example)
Alfiz does not provide a Clerk adapter package. You write a thingetAlfizSession helper that calls Clerk’s auth() and passes the user ID to createSession. This keeps the integration explicit and avoids a hard dependency on any specific Clerk version.
View-as: admin preview
Administrators holdingalfiz_internal.access.view_as can preview the product as either a role or an individual user. createSession enforces this gate automatically, so passing a non-null viewAs without the permission throws AccessDeniedError.
assertCanViewAs
If you need to check preview eligibility independently (for example, to show or hide the “view as” UI affordance), call assertCanViewAs directly:
AccessDeniedError when the actor does not hold alfiz_internal.access.view_as, or when the catalog was built with includeAlfizInternal: false.
Service principals
For machine-to-machine authentication in background jobs, CI pipelines and internal services, Alfiz provides a local service-key shim that validates shared bearer tokens server-side.Creating the shim
Verifying a service request
service:<serviceId>. Grant to it like any other subject:
The env format
parseServiceKeysEnv parses ALFIZ_SERVICE_KEYS in the format:
Key length requirement
Keys must be at least 16 characters.createServiceKeyShim throws at startup if any key is shorter.
Client-reach guard
Service keys must never reach the browser. AddALFIZ_SERVICE_KEYS to forbidClientIdentifiers in your alfiz-verify.config.json so the build fails if the key module ever becomes client-reachable: