Priority 70 sits after RejectListener (77) and before LoginListener (66), and both bounds are load-bearing: - After 77 so a rate-limited IP never reaches a ceremony. Passkeys cannot be used to sidestep a lockout (D3), which is the point of the reviewer's third clarification. - Before 66 because LoginListener treats any POST to the auth subdomain as a login attempt. A ceremony finish body has no username/totp, so Payload::load() returns null and the request would be scored as a failed login, burning a rate-limit token for every legitimate passkey login. Verified in the live container rather than assumed: debug:event-dispatcher confirms 77 -> 70 -> 66. Other properties asserted by tests: every header-bearing request gets a JSON response so fetch() callers never receive HTML; registration identity comes from the live session, never the request body; a failed ceremony is indistinguishable from a wrong TOTP code and spends the same shared budget; and begin is bounded by a separate resource guard that deliberately does not consume failure budget. The listener also marks its responses so SecurityHeadersListener can apply no-store: these are the only browser-facing 2xx this application produces, since the auth subdomain has no forward_auth in front of it. CSP gains publickey-credentials-get/-create only when passkeys are available, so the unavailable case stays byte-identical to before.
20 lines
654 B
YAML
20 lines
654 B
YAML
framework:
|
|
cache:
|
|
app: cache.adapter.filesystem
|
|
pools:
|
|
nonceCache:
|
|
adapters: cache.adapter.apcu
|
|
rateLimitCache:
|
|
adapters: cache.adapter.apcu
|
|
sessionCache:
|
|
adapters: cache.adapter.apcu
|
|
sessionStorage:
|
|
adapters: cache.adapter.filesystem
|
|
publicRateLimitCache:
|
|
adapters: cache.adapter.apcu
|
|
passkeyRateLimitCache:
|
|
adapters: cache.adapter.apcu
|
|
|
|
# Unique name of your app: used to compute stable namespaces for cache keys.
|
|
prefix_seed: digitaladapt/preauth
|