Skip to content

Identity contracts

This page lists the exact shapes involved in identity details: what your provider implements, what it receives, what /.cratis/me returns, and where each rule lives in the source. For the walkthrough, read Identity and How identity details are served.

Source: Source/Core/identity/IdentityDetailsProvider.ts. Import the type from @cratis/arc.core.

MemberTypeMeaning
detailsTypeA class with @field declarationsThe shape of details; Arc derives its schema from the fields
schemaz.ZodTypeAn explicit Zod schema for details; used instead of detailsType when both are set
provide(principal, context)Returns unknown or Promise<unknown>Returns the details, or undefined to deny the caller

A provider must declare detailsType or schema, and a provide function. Otherwise build() fails with Identity details require a provider schema (Source/Core/validateOptions.ts). Prefer detailsType: the proxy generator turns it into a frontend class.

RegistrationHowInstances
Decorated class@identityDetailsProvider() on a class, added with builder.add(...) or found by builder.discover(...)A new instance for every request, constructed with no arguments
Option objectidentityDetails: { detailsType, provide } or { schema, provide } in ArcApplication.createBuilder(...) or new ArcServer(...)The object you pass

Sources: Source/Core/identity/discoverIdentityDetails.ts, Source/Core/build/buildRegistered.ts, and identityDetails in Source/Core/ArcOptions.ts.

build() fails when you supply both an option object and a decorated class (Explicit and discovered identity details providers cannot be combined), and when discovery finds more than one decorated class (Multiple identity details providers found).

A decorated class has no constructor injection. Resolve services inside provide with currentServices(); Arc runs provide in a service scope created for the request and disposes it afterwards.

Principal (Source/Core/identity/Principal.ts), produced by your authentication handler or a native principal:

PropertyType
idstring
namestring, optional
rolesreadonly string[]
isAuthenticatedboolean
claimsunknown, optional
schemestring, optional

ExecutionContext (Source/Core/execution/ExecutionContext.ts, also exported from @cratis/arc.core) carries correlationId, principal, tenantId, remoteAddress, signal, and allowedSeverity. Tenant resolution has already run, so context.tenantId is the tenant this request selected.

Source: Source/Core/http/handleIdentity.ts. The endpoint is mapped only when a provider is registered (Source/Core/http/createRouteTable.ts).

PropertyValue
idprincipal.id
nameprincipal.name, or "" when the principal has none
isAuthenticatedAlways true
isAuthorizedAlways true
rolesprincipal.roles
detailsWhat provide returned, parsed by the provider’s schema

Every other outcome is a status code, not an identity with a false flag:

OutcomeStatus and body
No authenticated principal, or the credential was rejected401 {"error":"Unauthorized"}
Tenant missing or invalid, including a missing tenant with tenancy.required400 {"error":"Invalid tenant request"}
Not a member under tenancy.membershipClaim403 {"error":"Forbidden"}
provide returned undefined403 {"error":"Forbidden"}
provide threw, the details failed the schema, or the cookie would exceed 4096 bytes500 {"error":"An unexpected error occurred"}

Every answer carries Cache-Control: no-store. A 200 also sets .cratis-identity=<base64 JSON>; Path=/; SameSite=Lax, plus Secure on a trusted HTTPS transport. The cookie holds the same JSON as the body, with non-ASCII characters escaped. GET /.cratis/identity-details/schema returns the JSON Schema of details.

The server never reads .cratis-identity. Each call to /.cratis/me authenticates the request, resolves the tenant, and runs provide again (Source/Core/http/handleRequest.ts). The cookie is a display cache for the browser client, and commands and queries authorize against the verified principal only.

Arc on .NET works differently, and Arc for TypeScript deliberately does not copy it. In Arc 22.45.0, the .NET /.cratis/me endpoint returns a nonempty .cratis-identity cookie’s content before it consults the provider (Source/DotNET/Arc.Core/Identity/IdentityProvider.cs), and IIdentityProvider.ModifyDetails rewrites that cookie. Because the cookie is unsigned and editable by JavaScript, that path lets a browser choose what /.cratis/me reports. Arc for TypeScript has no such path, and no server-side way to modify details. To store a user preference, send a command, keep the value in your own storage, and return it from provide.

ConcernArc on .NETArc for TypeScript
Provider contractIProvideIdentityDetails.Provide(IdentityProviderContext)IdentityDetailsProvider.provide(principal, context)
Provider inputIdentityProviderContext: Id, Name, and Claims as string pairsPrincipal with roles and structured claims, plus the ExecutionContext
Provider outputIdentityDetails(IsUserAuthorized, Details)The details, or undefined to deny
Details shapeAny objectValidated against detailsType or schema
Discovery endpoints (/.cratis/commands, /.cratis/queries, /.cratis/users, /.cratis/tenants and /.cratis/identity-details/schema)Require authentication outside Development by default in 22.45.0Same default, opt-out, and optional roles; also covers /openapi.json. See Discovery access.
DependenciesConstructor injectioncurrentServices() inside provide
Denied caller403 from IsUserAuthorized: false403 from undefined
Cookie on the serverRead first when presentNever read
Modify detailsIIdentityProvider.ModifyDetailsNot available