Security Guide
UniAuth is built with a defense-in-depth security architecture. Multiple layers of protection work together to secure user accounts, tokens, and data — from password policies and multi-factor authentication to encrypted storage and post-quantum cryptography. This guide explains each security layer and provides a checklist for integrators.
Security Overview
UniAuth applies security controls at every level of the authentication lifecycle:
- Account creation — Password strength enforcement, breach database checking, email verification
- Authentication — Progressive account lockout, multi-factor authentication, passkey support
- Session management — Cryptographically signed sessions, device fingerprinting, inactivity timeouts
- Token issuance — Short-lived tokens, PKCE enforcement, refresh token rotation with replay detection
- Data at rest — AES-256-GCM encryption for sensitive fields, cryptographic hashing for tokens
- Transport — HTTPS enforcement, strict CORS policy, comprehensive security headers
- Monitoring — Login risk scoring, activity logging, webhook notifications for security events
Password Security
Strength Scoring
UniAuth evaluates password strength on a 0–4 scale using advanced pattern analysis that considers dictionary words, common patterns, keyboard sequences, and personal information. Passwords must score at least 2 ("fair") to be accepted:
| Score | Rating | Allowed |
|---|---|---|
| 0 | Very weak | No |
| 1 | Weak | No |
| 2 | Fair | Yes (minimum) |
| 3 | Strong | Yes |
| 4 | Very strong | Yes |
Passwords containing the user's email address, name, or username are penalized during scoring. This prevents users from creating passwords that are superficially long but easily guessable based on their profile information.
Breach Database Checking
During registration and password changes, UniAuth checks the chosen password against the HaveIBeenPwned breach database. This check uses a k-anonymity protocol — only the first 5 characters of the password hash are sent to the external service, so the full password is never revealed. If the password appears in a known breach, the user receives a warning but is not blocked from using it (the check is non-blocking).
Progressive Account Lockout
After multiple consecutive failed login attempts, the account is temporarily locked with escalating durations:
| Failed Attempts | Lockout Duration |
|---|---|
| 5 attempts | 1 minute |
| 10 attempts | 5 minutes |
| 15 attempts | 15 minutes |
| 20+ attempts | 1 hour |
The lockout counter resets upon a successful login. Passkey authentication bypasses the lockout mechanism, giving legitimate users an alternative way to access their account during a brute-force attack. Administrators can manually unlock accounts from the admin panel.
Token Security
Session Cookies
Session tokens are stored in cookies with the following security attributes:
- HttpOnly — Cannot be accessed by client-side JavaScript, preventing XSS-based token theft
- Secure — Only transmitted over HTTPS connections (enforced in production)
- SameSite=Lax — Prevents the cookie from being sent with cross-site requests, mitigating CSRF attacks
Never store access tokens in localStorage or sessionStorage. These storage mechanisms are accessible to any JavaScript running on the page. If your site has an XSS vulnerability, tokens in browser storage can be exfiltrated. Use httpOnly cookies or keep tokens in memory only.
Refresh Token Rotation
Refresh tokens are rotated on every use. Each token exchange invalidates the previous refresh token and issues a new one. If a rotated (already-used) refresh token is presented again, UniAuth detects potential token theft and revokes the entire token family, forcing re-authentication. See the Token Reference for full details.
Encryption at Rest
Sensitive data stored in the database is encrypted using AES-256-GCM authenticated encryption. This includes:
- Two-factor authentication secrets
- One-time verification codes
- OAuth provider access tokens
- Cryptographic signing keys
Password reset tokens, email verification tokens, and backup codes are stored as cryptographic hashes (SHA-256), not in plaintext or reversible encryption. Even a full database compromise would not reveal these values.
PKCE (Proof Key for Code Exchange)
UniAuth requires PKCE for all OAuth2 authorization code flows. PKCE (pronounced "pixy") prevents authorization code interception attacks by binding the token exchange to the same client that initiated the authorization request.
Only the S256 challenge method is supported (the plain method is not accepted). This means:
- Your application generates a random
code_verifier(43–128 characters) - Computes
code_challenge = base64url(SHA256(code_verifier)) - Sends the
code_challengewith the authorization request - Sends the original
code_verifierduring the token exchange
UniAuth verifies that the code verifier matches the challenge before issuing tokens. An attacker who intercepts the authorization code cannot exchange it without the original code verifier.
// Generate PKCE values (JavaScript)
function generateCodeVerifier() {
const array = new Uint8Array(64);
crypto.getRandomValues(array);
return btoa(String.fromCharCode(...array))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
async function generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const data = encoder.encode(verifier);
const hash = await crypto.subtle.digest("SHA-256", data);
return btoa(String.fromCharCode(...new Uint8Array(hash)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
}
const codeVerifier = generateCodeVerifier();
const codeChallenge = await generateCodeChallenge(codeVerifier);
// Store codeVerifier securely — you will need it for token exchangeCORS Policy
UniAuth enforces a strict Cross-Origin Resource Sharing (CORS) policy. By default, cross-origin API requests are blocked by the middleware layer. Only specific OAuth and SCIM endpoints allow cross-origin access, as they are designed to be called from external applications.
Endpoints Allowing CORS
POST /api/oauth/token— Token exchangeGET /api/oauth/userinfo— UserInfo endpointPOST /api/oauth/revoke— Token revocationPOST /api/oauth/introspect— Token introspectionGET /api/oauth/end-session— End session / logout/api/scim/*— SCIM provisioning endpointsGET /.well-known/openid-configuration— OIDC DiscoveryGET /.well-known/jwks.json— JSON Web Key Set
All other endpoints, including /api/auth/* (login, register, logout) and /api/user/* (profile, sessions), reject cross-origin requests. These endpoints are intended to be accessed from the same origin or from server-to-server calls.
Security Headers
UniAuth sets the following security headers on all responses to protect against common web attack vectors:
| Header | Value | Purpose |
|---|---|---|
X-Frame-Options | DENY | Prevents UniAuth from being embedded in iframes, blocking clickjacking attacks |
X-Content-Type-Options | nosniff | Prevents browsers from MIME-sniffing responses, reducing drive-by download attacks |
Strict-Transport-Security | max-age=31536000; includeSubDomains | Forces browsers to use HTTPS for 1 year, preventing protocol downgrade attacks (production only) |
Content-Security-Policy | frame-ancestors 'none'; base-uri 'self'; form-action 'self' | Prevents framing, restricts form submission and base URI to same origin |
Referrer-Policy | strict-origin-when-cross-origin | Limits referrer header to origin only on cross-origin requests, preventing URL leakage |
Permissions-Policy | geolocation=(), microphone=(), camera=() | Disables unnecessary browser APIs that could be abused by injected scripts |
Webhook Security
Every webhook delivery is signed with an HMAC-SHA256 signature using your webhook's shared secret. The signature is included in the X-UniAuth-Signature header.
The signature format is:
sha256=5d41402abc4b2a76b9719d911017c592e0f3c5a7d8b1c2e3f4a5b6c7d8e9f0a1Always verify the webhook signature before processing the payload. Use a constant-time comparison function to prevent timing attacks. See the Webhooks documentation for verification code examples in Node.js, Python, and other languages.
Warning: Never process a webhook payload without verifying the signature. An attacker who knows your webhook URL could send forged events to manipulate your application.
Post-Quantum Cryptography
UniAuth incorporates post-quantum cryptographic algorithms to future-proof security against advances in quantum computing. While current classical computers cannot break standard cryptography, quantum computers may eventually be able to. UniAuth prepares for this by including quantum-resistant algorithms today.
ML-DSA-44 Digital Signatures
Every session is signed with an ML-DSA-44 (FIPS 204) post-quantum digital signature. This provides cryptographic proof that the session was issued by your UniAuth instance and has not been tampered with. The signing keypair is automatically generated on first startup and stored securely with encryption at rest.
ML-KEM-768 Key Encapsulation
UniAuth uses ML-KEM-768 (FIPS 203) for key encapsulation, providing infrastructure for quantum-resistant key exchange. This mechanism supports secure key rotation and distribution across distributed deployments.
Both algorithms are NIST-standardized (2024) and represent the current best practice for post-quantum security. These protections work alongside traditional cryptography (RS256, HS256, AES-256-GCM) rather than replacing it.
Threat Detection
UniAuth performs real-time login risk scoring to detect potentially malicious authentication attempts. Each login is evaluated against the user's established behavioral baseline, and a risk score is computed based on the following signals:
| Signal | Description |
|---|---|
| New IP address | Login from an IP address that has never been associated with this account |
| New device or browser | Login from a previously unseen User-Agent string |
| Unusual login hours | Login at a time of day significantly outside the user's normal pattern |
| Failed attempt bursts | Multiple rapid failed login attempts preceding a successful login |
The system maintains per-user behavioral baselines that are updated with each login. When a login is flagged as high-risk, additional verification may be required. Administrators can review risk scores and flagged logins in the activity logs.
Data Retention and Cleanup
UniAuth automatically purges expired data to minimize the attack surface and comply with data minimization principles:
| Data Type | Retention Period |
|---|---|
| Expired sessions | 30 days after last activity |
| Expired tokens (password reset, email verification) | 7 days after expiration |
| One-time passwords (OTPs) | 24 hours after creation |
| Activity logs | 1 year |
The cleanup process runs automatically every 24 hours. This ensures that stale data does not accumulate and that expired credentials cannot be used even if the database is compromised.
Security Checklist for Integrators
If you are integrating your application with UniAuth, follow this checklist to ensure your implementation is secure:
- Always use HTTPS in production. All communication with UniAuth must be over TLS. HTTP connections are not accepted in production environments.
- Implement PKCE for all OAuth flows. PKCE is mandatory. Use the S256 challenge method. Store the code verifier securely between the authorization redirect and the token exchange.
- Store tokens securely. Keep access tokens in memory only (never in localStorage). Store refresh tokens in httpOnly cookies or secure server-side storage. Never log or expose tokens in URLs.
- Verify webhook signatures. Always validate the
X-UniAuth-Signatureheader using a constant-time comparison before processing any webhook payload. - Enable MFA for admin accounts. All administrator and moderator accounts should have multi-factor authentication enabled. UniAuth supports TOTP (authenticator apps), SMS codes, and email verification as second factors.
- Monitor activity logs. Regularly review the activity logs in the admin panel for unusual patterns such as logins from unexpected locations, repeated failed authentication attempts, or mass token revocations.
- Set up webhook alerts for security events. Subscribe to
user.login,user.password_changed, and other security-relevant events to detect anomalies in real time. - Rotate client secrets periodically. Generate new client secrets from the Developer Console on a regular schedule. Update your application configuration immediately after rotation.
- Use pairwise subject identifiers correctly. The
subclaim is unique per application. Use it as your user identifier. Do not attempt to correlate users across applications using this value — it is different for each app by design. - Implement proper CORS on your callback endpoints. Ensure your OAuth callback URL only accepts requests from expected origins. Validate the
stateparameter to prevent CSRF attacks during the OAuth flow. - Validate ID tokens fully. Always verify the RS256 signature, check
iss,aud,exp, andnonceclaims before trusting an ID token. See the Token Reference for validation examples. - Handle rate limits gracefully. Implement exponential backoff with jitter for all API calls. Cache token responses to minimize API usage. See the Rate Limits documentation for details.
Related documentation: Token Reference · Rate Limits · Error Reference · Webhooks · Multi-Factor Auth