Auth - developerz-ai/ultimate GitHub Wiki
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 { defineAuth, login, oauthLogin, postgresAuthAdapter } from '@ultimat3/auth';
export const auth = defineAuth({
adapter: postgresAuthAdapter(), // or memoryAuthAdapter()
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.
Signing in by handle (As of 2026-10): an app whose users are @ada names the lookup once โ
defineAuth({ adapter, handles: async (handle) => (await db.users.where({ handle }).one())?.id ?? null })
โ and calls login(auth, { handle, password }). The handle arrives trimmed and lowercased
(normaliseHandle), its failures count against a bucket of its own, and an unknown handle is the
same X_UNAUTHENTICATED after the same hash as a wrong password. The app's users row and the auth
user share one id: register(auth, { id, email, password }) creates the auth user under it. A handle
login on an app that declared no handles is X_CONFIG_INVALID. The deployed demo
(dummy/social-media-clone/apps/web/app/auth/) signs in this way.
| 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. The org bucket (orgMaxAttempts, default 100 per window) can be exhausted by someone who knows about 20 of an org's addresses, locking its password sign-in โ kept by decision (SECURITY.md) |
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, which also revokes the
user's live API keys. A passwordHash change through updatePrivileges ends every other session
of that user โ all of them when no session of theirs is passed, as in a reset.
github and google 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.
Rule (As of 2026-10) |
|
|---|---|
redirect_uri origin |
oauthLogin(auth, { baseUrl }), else APP_URL. With neither the start leg answers X_ENV_MISSING โ never the request's Host
|
| a refused leg's body |
code, title, docs, one fixed cause, fix: x errors explain <CODE> --json. The authored cause and fix are the auth.oauth.refused log line |
discoverOAuthProvider |
refuses a document whose issuer is not the one asked for (a trailing slash aside) |
apple |
not a built-in since 26.0.0 (#709): Apple POSTs its callback and both callback routes are GET, so it could never complete a sign-in. An app that needs it registers its own with registerOAuthProvider
|
| Capability | Calls |
|---|---|
| TOTP + recovery codes |
enrolTotp, saveTotpSecret, generateRecoveryCodes. The secret is sealed at rest under the app's master key and a plaintext one is refused, never read (X_MFA_SECRET_UNSEALED; x auth seal-mfa seals rows from before); a recovery code is consumed in one statement, so it works once. A password proven with a factor outstanding is X_MFA_REQUIRED, whose meta.challenge is a sealed five-minute credential; completeMfa(auth, challenge, code) finishes the sign-in, meters the guess against the same lockout buckets a password guess spends, and refuses a spent code |
| 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 the key's and never the owner's roles (X_API_KEY_INVALID). A key is its owner's credential: a missing or disabled owner refuses it on the next use, a wildcard scope (*, <resource>:*) is refused at issue, and an owned key keeps only the scopes its owner's grants cover โ pass grantsOf to expand roles (packages/auth/README.md); a key with no userId is unchanged. apiKeyResolver(() => store) is both as the resolveToken a bearer mount and defineAppMcp() take: { actor, scopes } for a live key, one null for every wrong one, and a store fault left a throw. In a test the store is memoryAuthAdapter()
|
| 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.