Files
preauth/docs/examples/.env.example
T
lyra ffb824c652 Add the passkey listener at priority 70
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.
2026-09-27 10:50:09 +00:00

108 lines
4.8 KiB
Bash

# --- main options ---
# URI containing secret and config for TOTP, which determines the token to login
# app will generate one, if not provided, but you should copy it to your .env file
# format: "otpauth://totp/<label>?secret=<secret-key>"
#TOTP_URI='' # blank to have the app generate one at random
# how long will someone stay logged in, measured in seconds, zero for DEFAULT
#COOKIE_TTL=2592000 # default 30 days
# we can use a central auth, so that users only need to login once to have access to
# multiple services. Requires using sub-domains under the same domain.
# IE: if enabled have "service-one.example.com" redirect "auth.example.com", and after
# successful auth, user can visit "service-two.example.com" without having to login again.
#SUBDOMAIN_REDIRECT=false # default disabled, boolean
#AUTH_SUBDOMAIN='' # blank, hostname we send user to, to see login page
# --- passkey authentication ---
# Passkeys (Touch ID / Windows Hello / security keys) as an alternative to TOTP.
#
# REQUIRES central authentication (SUBDOMAIN_REDIRECT=true plus AUTH_SUBDOMAIN)
# and HTTPS. Passkeys are bound to a relying party that spans the base domain,
# which only exists when central auth is configured; and browsers refuse to run
# a ceremony over plain HTTP.
#
# Enabling this without central auth is a hard error: the container fails at
# start-up (cache:warmup) rather than offering a passkey button that cannot work.
#
# There is deliberately no option to allow an http:// origin, and none to relax
# the requirement for local development. See the README for the local TLS setup.
#PASSKEY_ENABLED=false # default disabled, boolean
#PASSKEY_RP_NAME='' # blank to use TITLE
#PASSKEY_USER_VERIFICATION='required' # required | preferred | discouraged
#PASSKEY_TIMEOUT=60000 # ceremony timeout in milliseconds
#PASSKEY_BUTTON_NAME='Sign in with a passkey'
#PASSKEY_REGISTER_NAME='Register this device as a passkey'
#PASSKEY_BEGIN_BURST_COUNT=30 # ceremonies one caller may start per window
#PASSKEY_BEGIN_BURST_TIME=60 # window for the above, in seconds
# --- extra options ---
# how long do we allow *ALL* traffic from an ip address after successful login
# could be useful if you have a system which does not handle cookies
#IP_TTL=0 # default disabled, time in seconds
# once blocked, do we respond with "I'm a teapot", false to use "Too many requests"
#TEAPOT=true # default enabled, boolean
# --- server / worker options ---
# (container/deployment only) restart each FrankenPHP worker thread after
# this many requests, containing memory growth across long uptime;
# matching the default from the old runtime/frankenphp-symfony package.
# 0 disables restarts. consumed by the Caddyfile, not the PHP app.
#MAX_REQUESTS=500 # default 500
# --- remote-user header ---
# Controls the value sent in the Remote-User header on successful auth.
# session: the session id (default, backward-compatible)
# static: a fixed string (set via REMOTE_USER_STATIC)
# mapped: look up session id in REMOTE_USER_MAP (format: id1:user1,id2:user2)
# none: do not send the Remote-User header at all
#REMOTE_USER=session
#REMOTE_USER_STATIC=authenticated
#REMOTE_USER_MAP=''
# --- rate limiting ---
# Note: rate limiting can *NOT* be disabled, but you could allow hundreds of logins a second
# rate limiting, default is the lower of 2 per 30 seconds or 10 per hour
#BURST_COUNT=2 # 2 per 30 seconds
#BURST_TIME=30 # seconds
#UPPER_COUNT=10 # 10 per hour
#UPPER_TIME=3600 # seconds (1 hour)
# --- public access (rate-limited, no auth required) ---
# Comma-separated path patterns for public access. Wildcards:
# * matches any chars within one path segment (not crossing /)
# ** matches any chars including / (crosses path segments)
# Optional host prefix: host.example.com/path/**
# When empty (default), the feature is fully disabled.
#PUBLIC_PATHS=''
#PUBLIC_BURST_COUNT=100 # max requests per burst window per IP
#PUBLIC_BURST_TIME=60 # burst window in seconds
#PUBLIC_UPPER_COUNT=500 # max requests per sustained window per IP
#PUBLIC_UPPER_TIME=3600 # sustained window in seconds (1 hour)
# --- styling options ---
#TITLE='Pre-Authentication System'
#BG_COLOR='#029386' # teal
#FG_COLOR='#ffffff' # white
#ERROR_COLOR='#ffb16d' # apricot (light orange)
#ID_NAME='Session ID'
#TOKEN_NAME='Authentication Token'
#SUBMIT_NAME='Submit'
#ERROR_MESSAGE='Unsuccessful login attempt'
# title and message to use on block page, if teapot is true
#TEAPOT_TITLE="I'm a teapot"
#TEAPOT_MESSAGE='I refuse to brew coffee'
# title and message to use on block page, if teapot is false
#TOO_MANY_TITLE='Too many requests'
#TOO_MANY_MESSAGE='Try again later'
# --- debug options ---
#SHELL_VERBOSITY=0 # set to "3" to log debug