OxL Reference

13.2 Authentication

oxl
enum Principal {
  User(id: UserId, roles: Set<AppRole>, claims: Map<str, str>, mfa: bool, tenant: str?)
  Service(name: str, role: str)
  Node(id: NodeId)
  Anonymous
}
  • Providers are set in cluster { auth { … } }: jwt(issuer, audience?, jwks?), oidc(issuer, clientId), apikey, session(cookie: name). User roles: roles from claim("roles") as AppRole.

Implementation-defined. OIDC (D-8055; DIV-025; D-4739). An optional feature: the provider oidc(issuer, clientId). This implementation does not contain it; JWT verification by jwks or a given key (jwt(…)) is supported.

  • apikey — a key in a header (apikey(header: h), x-api-key by default) or in Authorization: ApiKey …; the server stores only the hash of the key, a key yields a Principal.User. session(cookie: name) — a server-side session: createSession(user, roles, ttl) -> str | SessionError (std.rpc) creates a session and returns the identifier that the client carries in the cookie name; endSession(id) ends it; setting Set-Cookie is up to the application. Order: Bearer (jwt) → key → session cookie; the first one presented decides; a presented but invalid credential is UNAUTHENTICATED, none at all is Anonymous. Credential headers do not get into ctx.meta and are not passed on (D-8065; D-8200..D-8202, D-8208).

Implementation-defined. Storage of keys and sessions (D-8065; DIV-025; D-8200, D-8201). This implementation: keys are a JSON file with SHA-256 (OXL_AUTH_APIKEYS / --auth-apikeys; fields id, sha256, sub, roles, tenant, mfa, claims, expires), compared in constant time, reloaded when the file changes; no file means every key is rejected; a session is a record {exp, principal} under the hash of the identifier in the replicated storage, lifetime 1 ms..30 days.

  • Verification is performed before entering the handler. A failure is AccessDenied (UNAUTHENTICATED).
  • Services and nodes are identified by their mTLS certificate.
  • Delegation: outgoing calls from a handler carry the original user (ctx.caller) and the calling service (ctx.via). To narrow the rights: asScoped(caller.scoped([…])) { … }; to call on behalf of your own service: asService { … }. Calls of external services (S.connect(addr)) do NOT carry the identity of the user or of the service: the headers x-oxl-principal and x-oxl-user are not sent there, and the authorization of the inbound call is not forwarded (the application puts its own credential into ctx.meta if needed) (D-8066; D-8300).

Implementation-defined. Type of the user identifier (D-8056; DIV-049; D-2905). UserId in Principal.User and in userId() is an application type (for example, distinct u64) that the standard library does not know. This implementation stores the identifier in Principal.User as str (the value of the JWT claim sub); Ctx.userId() and isUser accept and return str; the application parses it into its own UserId (in V.5 by the function callerId(ctx)).

Methods of Principal: isUser(id: UserId?) -> bool (false for none and for a non-user), has(role: AppRole) -> bool, service(name: str) -> bool, userId() -> UserId | AccessDenied. In handlers ctx.userId() is shorthand for ctx.caller.userId(); in policies the principal is available as caller.

cluster-auth

See also

Source: docs/spec/oxl-standard.en.md