import type { AuthConfig, AuthLogger } from '../types.js'; import type { BackupCodeRepository, InvitationRepository, PasskeyRepository, RefreshTokenRepository, UserRepository } from './adapters/types.js'; import type { EmailTransport } from './email/types.js'; export interface AuthDeps { config: AuthConfig; repos: { user: UserRepository; invitation: InvitationRepository; /** * Optional — required only when `config.refreshToken` is set. Pass the * Prisma adapter's `refreshToken` field, or an in-memory/Redis/Upstash * implementation via `createInMemoryRefreshTokenRepository(store)` or a * custom `RefreshTokenRepository`. */ refreshToken?: RefreshTokenRepository; /** * Optional — required only when `config.twoFactor` (TOTP 2FA) is wired. Pass * the adapter's `backupCode` field (in-memory/Prisma both ship one). Stores * the SHA-256-hashed recovery codes the 2FA flow issues at enable. */ backupCode?: BackupCodeRepository; /** * Optional — required only when the passkey route group * (`createPasskeyHandlers`) is mounted; that factory throws at wiring time * when it is missing. Pass the adapter's `passkey` field. */ passkey?: PasskeyRepository; }; /** * Optional — required only when a factory that sends mail is mounted. * `createRegisterHandler`, `createForgotPasswordHandler` and * `createChangeEmailHandler` mail whenever they act at all — the caller can * neither ask for nor opt out of the mail, and its failure never reaches * them — and therefore throw at wiring time when it is missing. * `createInvitationHandlers` mounts without one — its mail hangs on the * per-request `sendEmail` flag, and the copy-link flow (`inviteUrl` in the * `201`) needs no transport — and declines to mail only the invites that ask * for one, saying so in the log. An app that mounts none of the four * needs no transport at all. Pass `createConsoleEmailTransport()` * (`@urbicon-ui/auth/server/email/console`) in dev or * `createLettermintTransport` (or your own `EmailTransport`) in production. * * Because it is optional, code reading it off the bundle — a consumer's own * handler — sees `EmailTransport | undefined` and has to narrow. */ email?: EmailTransport; /** * Resolved log sink (`config.logger ?? console`) — `createAuthDeps` always * fills it, so handlers log operational failures through one seam instead * of hard-coding `console`. Three package internals sit outside a deps bundle * by design and take the sink as an optional argument instead: `validateCsrf` * (a standalone export a federated consumer calls from its own hook) and * `consumeChallenge` (in the deps-free WebAuthn core), both handed it by the * handle hook and the passkey factory; and the Prisma repository factories, * which run *before* any bundle exists — those default to `console`, the same * sink `config.logger ?? console` resolves to, so they only need * `createPrismaRepos(prisma, { logger })` when the app configures its own. */ logger: AuthLogger; } /** * Fail loud at wiring time when a feature is configured but its backing * repository is absent, instead of degrading silently at request time. The case * it covers is refresh-token rotation: with `config.refreshToken` set but * `repos.refreshToken` missing, `establishSession` would skip the refresh cookie * and the handle hook would decline to rotate — both without a trace, quietly * downgrading every session to access-token-only. Throwing here mirrors * `createPasskeyHandlers` (throws on a missing `repos.passkey`) and follows the * fail-loud-over-silent-fallback line. * * Reached from three call paths: both wiring entry points via * {@link assertAuthConfigValid}, and `establishSession` itself, which a consumer * can call directly with a hand-built `AuthDeps`. * * 2FA and passkeys are intentionally out of scope: 2FA already surfaces a * visible `feature_unavailable` 400 at request time when `repos.backupCode` is * absent, and the passkey factory already throws at wiring time — neither * degrades silently, so neither is the gap this closes. */ export declare function assertReposMatchConfig(config: AuthConfig, repos: { refreshToken?: RefreshTokenRepository; }): void; /** * Every wiring-time config check, in one call. `createAuthDeps` (the handler * bundle) and `createAuthHandle` (the hook) are wired independently and either * can be reached first, so both must validate — and each additional check used * to have to be remembered in both places. One door instead of three per entry * point: a new check is added here and both paths get it. * * Fails fast — the first throwing check wins, so a config with several problems * surfaces them one deploy at a time. */ export declare function assertAuthConfigValid(config: AuthConfig, repos: { refreshToken?: RefreshTokenRepository; }, logger: AuthLogger): void; /** * Assemble the auth dependency bundle, applying secure brute-force defaults to * the config (see {@link resolveSecurityDefaults}). The returned `config` * carries the resolved values — pass it on to `createAuthHandle` and the * handler factories so the whole app shares one resolved config. * * Once per process: bundles built for one `jwt.secret` share their rate-limit * counters per key and configured limit, so a second bundle costs a second * validation and resolution, and a key it configures differently counts on its * own. That call is warned about on the logger, once per secret, in every * deployment. */ export declare function createAuthDeps(deps: Omit, 'logger'>): AuthDeps;