auth/account_routes.ts

Account route specs for cookie-based session management.

Returns RouteSpec[] — caller applies them to Hono via apply_route_specs.

Four REST flows remain here; each has a concrete reason to stay REST rather than moving to auth/account_actions.ts:

  • POST /login — issues a signed Set-Cookie and pre-handler rate-limits by IP + per-canonical-account before password hashing.
  • POST /logout — clears the session cookie.
  • POST /password — cookie clear + revoke-all cascade; rate-limit-shaped error envelope on 429.
  • GET /verify — empty-body nginx auth_request probe. Programmatic callers should use the account_verify RPC action for the typed payload.

Session listing/revocation and API token CRUD are on the RPC endpoint — see auth/account_actions.ts. Signup is in auth/signup_routes.ts. Defaults are closed/safe: accounts are created through bootstrap, admin action, or invite.

view source

Declarations
#

7 declarations

AccountRouteOptions
#

auth/account_routes.ts view source

AccountRouteOptions import type {AccountRouteOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Per-factory configuration for account route specs.

inheritance

login_ip_rate_limiter

Rate limiter for login + password-change attempts, keyed by client IP. Pass null to disable. The distributed-spray backstop: login is the one surface that guesses *other* accounts' credentials, so this bucket is never refunded on success (see RateLimiter.reset). Password change shares it — it is password-bearing on the same account grain, and the Rust spine shares the same instance across both.

type RateLimiter | null

login_account_rate_limiter

Rate limiter for login attempts, keyed by submitted username. Pass null to disable.

type RateLimiter | null

max_sessions?

Max active sessions per account. Evicts oldest on login. Default 5, null disables.

type number | null

login_fail_floor_ms?

Minimum wall-clock time (ms) for login 401 responses. Set to 0 or a negative number to disable (e.g., in tests). Default DEFAULT_LOGIN_FAIL_FLOOR_MS.

type number

login_fail_jitter_ms?

Uniform jitter window (±ms) layered on the floor. Set to 0 to disable jitter while keeping the floor. Default DEFAULT_LOGIN_FAIL_JITTER_MS.

type number

connection_closer?

Live-connection closer — when set, the logout and password handlers eagerly close affected WebSocket sockets for the account BEFORE emitting the corresponding audit event. Mirrors the self-service action surface (see AccountActionOptions.connection_closer). When absent, only the listener-based close (transports_ws_auth_guard registered via audit.add_listener) runs.

type ConnectionCloser | null

bootstrap_status?

Runtime bootstrap status for the bundled GET /status route — when available, its unauthenticated 401 carries bootstrap_available: true so a fresh frontend can route to the bootstrap flow. Pass ctx.bootstrap_status (the live BootstrapStatus ref) so the flag tracks the one-shot bootstrap completing. Omit when no bootstrap flow is wired — /status is still served, just without the flag.

type { available: boolean }

AccountStatusOptions
#

auth/account_routes.ts view source

AccountStatusOptions import type {AccountStatusOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Options for the account status route spec.

path?

Override the default path (/api/account/status).

type string

bootstrap_status?

Runtime bootstrap status — when available, 401 responses include bootstrap_available.

type { available: boolean }

AuthSessionRouteOptions
#

auth/account_routes.ts view source

AuthSessionRouteOptions import type {AuthSessionRouteOptions} from '@fuzdev/fuz_app/auth/account_routes.js';

Shared options for route factories that create sessions.

Extended by AccountRouteOptions and SignupRouteOptions. Consumers can destructure these from AppServerContext once and spread into multiple factories.

The per-IP limiter is deliberately *not* here. Each auth surface names its own (login_ip_rate_limiter, signup_ip_rate_limiter, bootstrap_ip_rate_limiter) because the buckets are monotone within their window — see RateLimiter.reset. A shared base field made one instance the path of least resistance, which let a failure on any surface spend the budget that bounds guessing on every other one.

session_options

type SessionOptions<string>

create_account_route_specs
#

auth/account_routes.ts view source

(deps: RouteFactoryDeps, options: AccountRouteOptions): RouteSpec[] import {create_account_route_specs} from '@fuzdev/fuz_app/auth/account_routes.js';

Create account route specs for session-based auth.

The returned specs cover the REST flows that stay after the RPC migration: /status (account info + bootstrap availability), /verify (nginx auth_request shim), /login, /logout, /password. /status is bundled here (relative path, prefixed to /api/account/status by the caller) so every account surface serves it, matching the Rust account_router. Self-service session/token management is on auth/account_actions.ts.

deps

stateless capabilities (keyring, password, log)

options

per-factory configuration (session_options, login_ip_rate_limiter, login_account_rate_limiter, bootstrap_status)

returns

RouteSpec[]

route specs (not yet applied to Hono)

create_account_status_route_spec
#

auth/account_routes.ts view source

(options?: AccountStatusOptions | undefined): RouteSpec import {create_account_status_route_spec} from '@fuzdev/fuz_app/auth/account_routes.js';

Create the account status route spec.

Handles both authenticated and unauthenticated requests:

  • Authenticated: returns {account} with 200
  • Unauthenticated: returns 401 with optional bootstrap_available flag

This eliminates the need for a separate /health fetch on page load — the frontend gets both session state and bootstrap availability in one request.

options?

optional configuration (bootstrap_status for bootstrap detection)

type AccountStatusOptions | undefined
optional

returns

RouteSpec

a single account status route spec

DEFAULT_LOGIN_FAIL_FLOOR_MS
#

auth/account_routes.ts view source

250 import {DEFAULT_LOGIN_FAIL_FLOOR_MS} from '@fuzdev/fuz_app/auth/account_routes.js';

Default minimum wall-clock time (ms) for a login failure (401) response.

Picked to exceed the p99 of every 401 code path (Argon2id dominates at ~100ms, plus DB + overhead). The handler races failure work against sleep(floor + jitter) via await, so observed response time = max(work, delay). Found-vs-not-found and rate-limit-skipped-vs-not paths converge. Only 401 is padded — 429 stays fast by design to keep rate-limit DoS handling cheap.

DEFAULT_LOGIN_FAIL_JITTER_MS
#

auth/account_routes.ts view source

25 import {DEFAULT_LOGIN_FAIL_JITTER_MS} from '@fuzdev/fuz_app/auth/account_routes.js';

Default uniform jitter window (±ms) layered on the floor.

Random jitter prevents a stable clamp point from leaking whenever a path occasionally exceeds the floor. Math.random is sufficient — we only need unpredictability of the exact delay, not cryptographic guarantees.

Depends on
#

Imported by
#