# Authentication security guidance

## Required production controls

1. Use a cryptographically random authentication HMAC secret of at least 32 characters.
2. Use `SqlAuthStore` or another durable shared `AuthStore`; `MemoryAuthStore` is for development and tests.
3. Protect TOTP seeds and OAuth tokens with `createAuthSecretProtector()` and a rotated `@wrnexus/encryption` keyring.
4. Queue verification delivery and never log OTP codes, raw one-time tokens, reset URLs, or provider secrets.
5. Rate-limit registration, login, verification, OTP, reset, magic-link, invitation, passkey, and impersonation endpoints.
6. Accept CAPTCHA success only from a server-side verifier such as `captchaGuard()`.
7. Keep package CSRF verification enabled for cookie-authenticated unsafe requests unless an equivalent gateway control replaces it.
8. Validate every post-authentication redirect as same-origin.
9. Use HTTPS, `Secure` and `HttpOnly` session cookies, an appropriate `SameSite` policy, strict security headers, and trusted-proxy configuration.
10. Persist security events in a durable, access-controlled audit sink.
11. Require a reason and explicit authorization policy for impersonation and show a persistent banner while it is active.
12. Require recent or step-up authentication before especially sensitive account-management actions when the surrounding product policy demands it.

## Passwords and lockout

Passwords use the framework password API. Keep the default 12-character minimum or raise it, and configure a breach provider in production. Browser validation is only a usability layer; server validation and engine policy remain authoritative.

Temporary failed-login locks preserve the previous account state. A valid password reset clears only a failed-login lock; it does not reactivate an administratively locked, disabled, or deleted account.

## Tokens and OTP

Reset, verification, invitation, magic-link, and MFA transaction tokens are HMAC-hashed at rest, expire, and are one-use. OTP challenges are bound to verification, login, or MFA purpose and have attempt limits. Email/SMS MFA can use only verified linked identities.

Durable custom stores should consume one-time credentials atomically, preferably in the same transaction as the protected state change. The generic `AuthStore` interface exposes read/update operations and cannot by itself guarantee compare-and-set behavior across concurrent processes.

Public reset, magic-link, passwordless-OTP, and verification-request endpoints return generic responses to reduce account enumeration. Rate limiting and delivery-abuse controls are still required.

For login forms, pair `captchaGuard({ verifiedForMs })` with
`<Captcha resetOnError="false" />` so a successful human check can survive an
incorrect-password retry. The grant remains session-bound and action-bound;
authentication rate limits, risk blocking, and account lockout remain required.

## Account recovery

Disabled, deleted, and administratively locked accounts cannot complete verification or password recovery with previously issued tokens. Password reset revokes active sessions. Recovery-code regeneration invalidates earlier unused codes.

## Sessions and trusted devices

The WRNexusJS session container is regenerated when an auth session is established. Auth sessions have idle and absolute expiry. Trusted-device values should be opaque, stable identifiers generated by the application—not high-entropy browser fingerprint profiles containing unnecessary personal data.

## TOTP and recovery codes

TOTP validation rejects reused counters. Encrypt TOTP seeds with `secretProtector`. Recovery codes are displayed once and stored only as hashes.

## Passkeys

The browser runtime does not perform cryptographic verification. The configured `PasskeyProvider` must verify the challenge, RP ID, exact origin, signature, user presence or verification, counter, and credential ownership. Require HTTPS outside localhost.

The default challenge store is memory-only. Use a shared Redis, SQL, or equivalent `PasskeyChallengeStore` when requests can reach more than one process. Passkeys are implemented as strong passwordless sign-in; they are not currently a selectable method in the second-step MFA challenge component.

## OAuth

Use PKCE, state, nonce, and callback validation from `@wrnexus/oauth`. Encrypt provider tokens before durable storage. Never auto-link an OAuth account solely from an unverified provider email.

## Database integrity

Run both package migrations in order. Production schemas should preserve the included unique constraints for normalized identities, passkey credential IDs, trusted-device fingerprints, token hashes, and OAuth provider account IDs.

Registration and one-time credential state changes should run in transactions when a custom store supports them. This prevents partial writes and concurrent replay beyond what a generic multi-operation store can guarantee.

## Impersonation

`authorizeImpersonation` defaults to deny. Restrict it to named roles, require a reason, preserve the actor session, display `ImpersonationBanner`, and notify the organization when policy requires it. Apply a separate rule before allowing an actor to impersonate a more privileged target.

## Delivered links

Use `AuthEngineOptions.tokenUrl` to map one-time credentials to application pages. Treat the callback as trusted server configuration, use HTTPS production origins, and do not log raw tokens or generated URLs.
