-
Notifications
You must be signed in to change notification settings - Fork 0
Auth
The output of authentication is an Actor. Nothing downstream authorizes on a session row, a
user row or an api key: HTTP, actions, jobs and MCP all read ctx.actor and hand it to the one
policy system (Policies and authz). Package @ultimat3/auth (tier 2) — the
full reference is
packages/auth/README.md;
the walk-through is Tutorial 3.
import { BuiltinAdapter, defineAuth, login, oauthLogin } from '@ultimat3/auth';
export const auth = defineAuth({
adapter: new BuiltinAdapter(), // or MemoryAdapter
session: { absoluteTtlMs: 30 * 864e5, idleTtlMs: 7 * 864e5 },
password: { minLength: 12 },
mfa: { issuer: 'Acme' },
providers: ['github', 'google'], // required to serve any OAuth route — the default is []
});
const { actor, token, cookie } = await login(auth, { email, password, ip });
const { start, callback } = oauthLogin(auth); // /auth/oauth/:provider and its callbackThe tables (x_users, x_sessions, x_accounts, x_verifications, x_api_keys) are created by
every boot; an app writes no migration for them.
| Property | How |
|---|---|
| opaque tokens | only sha256(secret) reaches the database |
| two expiries, independent | absolute and idle; activity never moves the absolute ceiling (X_SESSION_EXPIRED names which) |
| not a write per request |
lastSeenAt moves at most once per idleSlideMs (default idleTtlMs / 20); a changed IP or user agent is written at once |
| revocation is immediate | the user row is re-read on every request, so a revoked role takes effect on the next one |
| the cookie |
__Host-x_session, HttpOnly, Secure, SameSite=Lax, Path=/, Max-Age
|
| brute force | per-IP, per-account and per-org buckets (X_ACCOUNT_LOCKED); every login failure is one indistinguishable X_UNAUTHENTICATED. rateLimit: { scope: 'shared' } makes the count one for the fleet |
Signing out is logout(auth, token) for the row plus signOutHeaders() on the response: it
expires the session cookie and sends Clear-Site-Data: "cache", "storage"
(SIGN_OUT_CLEAR_SITE_DATA), so the browser drops the previous principal's IndexedDB, storage,
service worker and cached pages. Revocation at scale is revokeUserSessions, revokeOrgSessions,
revokeSessionsCreatedBefore (after rotating a secret) and disableUser.
github, google and apple ship; registerOAuthProvider adds one. PKCE is mandatory on every
provider — usesPkce: false does not typecheck — and the handshake is a signed __Host- cookie
bound to its provider. An id token is verified against the provider's JWKS. An identity links to an
existing account only when both the provider and that account have verified the address
(link: 'verified-email', the default); 'never' is the only alternative. Credentials are
<PROVIDER>_CLIENT_ID / <PROVIDER>_CLIENT_SECRET.
| Capability | Calls |
|---|---|
| TOTP + recovery codes |
enrolTotp, verifyTotp, createTotpReplayGuard, generateRecoveryCodes, redeemRecoveryCode — pure functions; the secret and the hashes are the app's rows. A password proven with a factor outstanding is X_MFA_REQUIRED
|
| email verification, password reset |
issueVerification / consumeVerification; a token is consumed only when its hash matches, so a wrong guess cannot burn the victim's live link. The mail is sent through an injected MailSender (Mail) |
| API keys — how an agent authenticates |
issueApiKey → ult_<env>_<id>_<secret>, shown once; verifyApiKey + apiKeyActor give a kind: 'agent' actor whose scopes are exactly the key's, never the owner's roles (X_API_KEY_INVALID) |
| service-to-service |
verifyWorkloadToken reads a Kubernetes service-account token, a SPIFFE JWT-SVID or a cloud IMDS token (they are all one JWT); actorFromService gives a kind: 'service' actor. mTLS is the mesh's job |
Every auth code — X_UNAUTHENTICATED, X_SESSION_EXPIRED, X_MFA_REQUIRED, X_OAUTH_*,
X_ACCOUNT_LOCKED, X_API_KEY_INVALID and the rest — is in Error codes.
Ultimate — v22.1.0 As of 2026-09. Stable API, semver from here. MIT licensed. What npm serves is npm view @ultimat3/core version, never this line.
This footer is the only page that stamps a version. It renders under every wiki page, so one release bumps one line; a stamp on a second page is 46 hand-copies of one fact, and every one of them goes stale on the next tag.
Repository · Issues · Changelog · llms.txt
Edits to these pages are synced from wiki/ in the repository — change the file there, not the wiki, or the next sync overwrites it.
Start
Tutorials
- 1 · First app
- 2 · First feature
- 3 · Auth and admin
- 4 · Jobs and realtime
- 5 · Deploy free
- 6 · Growing up
Primitives
- The eight primitives
- Building your own base
- Actions
- Entities and migrations
- Policies and authz
- Queries and live queries
- Client data
- Jobs and workflows
- Scheduled tasks
- Routes and render modes
Capabilities
- Realtime
- Caching and invalidation
- Batching and preloading
- N+1 detection
- PWA and offline
- MCP and AI
- Agents
- Admin dashboard
- Scraping
- Auth
- Notify
- Storage and uploads
- Feature flags
- SEO
Cross-cutting
- I18n
- Theming
- UI components
- Interface rules
- Timezones and dates
- Money
- Resource management
- Migrations and backfills
- Testing
Reference