Overview
Step-up authentication requires users to re-verify their identity before performing sensitive operations. PassAgent enforces a configurable time window (default: 5 minutes) during which a user is considered “recently authenticated.” Once the window expires, any privileged action triggers a re-authentication challenge before proceeding. This mechanism is layered on top of session authentication. A valid session token alone is not sufficient for high-risk operations — the user must also have authenticated within the step-up window.Time-window model
When a user successfully logs in, verifies 2FA, or completes a re-authentication prompt, PassAgent records the timestamp in Redis viamarkAuthenticated(userId). Every sensitive API route calls requireRecentAuth() to check whether the recorded timestamp falls within the allowed window.
| Parameter | Value |
|---|---|
| Default max age | 300 seconds (5 minutes) |
| Redis key format | stepup:last_auth:{userId} |
| Key TTL | 86,400 seconds (24 hours) |
| Configurable per-route | Yes (maxAgeSeconds parameter) |
maxAgeSeconds, the API returns a 401 response with the STEP_UP_AUTH_REQUIRED error code and custom headers:
x-require-reauth: truex-reauth-max-age: 300
Every step-up challenge is recorded in the audit log with the action
step_up_auth_required, including the operation that triggered it, the user’s IP address, and the elapsed time since last authentication.Risk-adaptive enforcement
Beyond the fixed time window, PassAgent applies risk-adaptive step-up authentication powered by the anomaly detector. TheenforceRiskAdaptiveStepUp() function evaluates behavioral signals in real time and can escalate requirements or block access entirely.
Anomaly signals
The behavior analysis engine scores each request against the user’s established profile. Signals that contribute to the anomaly score include:| Signal | Detection | Resulting action |
|---|---|---|
| Brute force | 5+ failed auth attempts in 5 minutes | block |
| Impossible travel | Geo-IP distance / time implies physically impossible movement | step_up_auth |
| Suspicious travel | Unusual but not impossible location change | warn |
| New device | Unrecognized device fingerprint | step_up_auth |
| Revoked device | Previously trusted device that was explicitly revoked | block |
| Bulk operations | 50+ vault reads/exports in 60 seconds | step_up_auth |
| IP spray | Single IP targeting multiple accounts | block |
| Access anomaly | Behavioral score exceeds threshold | step_up_auth or block |
Tightened window
When a risk signal triggers step-up auth, the allowed window is reduced from the default 300 seconds to 60 seconds. This means the user must have re-authenticated in the last minute, not the last five minutes. The response includes an additional headerx-risk-adaptive-step-up: true so the client can display appropriate context to the user.
2FA verification layer
For users with two-factor authentication enabled, PassAgent enforces an additional verification layer through therequire2FA() guard. This operates independently of the step-up time window and uses a signed cookie to track verification state.
User triggers sensitive action
The API route calls
require2FA(userId) after confirming session auth.Check 2FA status
If the user has not enabled 2FA (
user_2fa.enabled = false), the guard passes through. Backward compatibility is maintained for accounts without 2FA.Validate cookie
The
pa_2fa_verified cookie is checked. It contains a base64url-encoded payload signed with HMAC-SHA256 using a dedicated secret (TWOFA_VERIFIED_COOKIE_SECRET).Server-side verification
Even if the cookie signature is valid, PassAgent verifies that a corresponding Redis record (
2fa:verified:{userId}) still exists. This allows server-side invalidation on password change or logout.| Parameter | Value |
|---|---|
| Cookie name | pa_2fa_verified |
| Cookie TTL | 15 minutes |
| Cookie flags | httpOnly, secure (production), sameSite: strict |
| Signature algorithm | HMAC-SHA256 |
| Server-side record | Redis key 2fa:verified:{userId} with matching TTL |
The 2FA cookie uses dual validation: the HMAC signature prevents client-side tampering, while the Redis record allows immediate server-side revocation. Calling
invalidate2FAVerification(userId) clears the Redis record, rendering the cookie useless even if it has not expired.Operations requiring step-up auth
The following operations require recent authentication. Some routes use the default 5-minute window; others apply risk-adaptive enforcement with a tighter window.Vault operations
Vault operations
- Decrypt and view a stored password
- Export vault data
- Bulk credential operations
- Delete vault items
Account management
Account management
- Change master password
- Enable or disable 2FA
- Manage trusted devices
- Transfer family admin privileges
Sharing and recovery
Sharing and recovery
API usage
To protect an API route with step-up authentication, callrequireRecentAuth() early in the handler:
enforceRiskAdaptiveStepUp():
Security properties
- Defense in depth: step-up auth, 2FA verification, and anomaly detection operate as independent layers. Compromising one does not bypass the others.
- Fail-closed: if the Redis timestamp is missing or unreadable, the system treats the user as unauthenticated and requires re-verification.
- Audit trail: every step-up challenge and anomaly detection event is recorded with full context (IP, user agent, action, elapsed time).
- Server-side revocation: 2FA verification can be invalidated instantly from the server without waiting for cookie expiration.