auth/fail_floor.ts

The wall-clock floor on auth-denial responses, shared by login and signup.

Without a floor on denial paths, an attacker can tell cheap rejections (signup's no_matching_invite, which bails before Argon2 and the transaction) from expensive ones (signup_conflict, Argon2 + transaction + rollback) by response time, and use the gap as a username-enumeration oracle. Each route picks its own floor and jitter; this module holds the delay and the loud warning when a floor is turned off. Twin of the Rust spine's fuz_auth::timing.

429 responses take no floor — they short-circuit before any work, and their timing is no enumeration signal.

view source

Declarations
#

5 declarations

FailFloor
#

auth/fail_floor.ts view source

FailFloor

type "login" | "signup"

import type {FailFloor} from '@fuzdev/fuz_app/auth/fail_floor.js';

Which denial-timing floor a disabled-floor banner names.

LOGIN_FAIL_FLOOR_DISABLED_BANNER
#

auth/fail_floor.ts view source

"!!! LOGIN FAILURE TIMING FLOOR DISABLED — test-only mode. A production server must never print this." import {LOGIN_FAIL_FLOOR_DISABLED_BANNER} from '@fuzdev/fuz_app/auth/fail_floor.js';

The stderr line a process prints when the login failure floor is off — the Rust spine's text.

SIGNUP_FAIL_FLOOR_DISABLED_BANNER
#

auth/fail_floor.ts view source

"!!! SIGNUP FAILURE TIMING FLOOR DISABLED — test-only mode. A production server must never print this." import {SIGNUP_FAIL_FLOOR_DISABLED_BANNER} from '@fuzdev/fuz_app/auth/fail_floor.js';

The stderr line a process prints when the signup failure floor is off — the Rust spine's text.

start_fail_floor
#

auth/fail_floor.ts view source

(floor_ms: number, jitter_ms: number): Promise<void> import {start_fail_floor} from '@fuzdev/fuz_app/auth/fail_floor.js';

Start the denial floor: a promise resolving floor_ms ± jitter_ms from now, or at once when floor_ms is 0 or less (the floor disabled).

Call it before the denial path's work and await it after, so the observed response time is max(work, delay), not work + delay. The timer starts at the call. Uniform jitter keeps a stable clamp point from leaking when a path occasionally exceeds the floor; Math.random suffices, since only the exact delay must be unpredictable.

floor_ms

the floor

type number

jitter_ms

the jitter window on either side of the floor; 0 or less for none

type number

returns

Promise<void>

a promise resolving when the floor has passed

warn_if_fail_floor_disabled
#

auth/fail_floor.ts view source

(floor: FailFloor, floor_ms: number, log: Logger): void import {warn_if_fail_floor_disabled} from '@fuzdev/fuz_app/auth/fail_floor.js';

Announce, once per module instance per floor, that a denial-timing floor is off.

Called by the route factories (create_account_route_specs, create_signup_route_specs) at assembly when the configured floor is 0 or less. A disabled floor turns off the username-enumeration defense, so — like the rate limiters' announce_rate_limiters_disabled — it goes to stderr as well as the log: a deployment that copied a test binary's 0 shows it in its first lines of output. A positive floor prints nothing.

floor

which floor

floor_ms

the configured floor

type number

log

where the warning is logged beside the stderr banner

type Logger

returns

void

Imported by
#