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:

ScoreRatingAllowed
0Very weakNo
1WeakNo
2FairYes (minimum)
3StrongYes
4Very strongYes

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 AttemptsLockout Duration
5 attempts1 minute
10 attempts5 minutes
15 attempts15 minutes
20+ attempts1 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:

  1. Your application generates a random code_verifier (43–128 characters)
  2. Computes code_challenge = base64url(SHA256(code_verifier))
  3. Sends the code_challenge with the authorization request
  4. Sends the original code_verifier during 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 exchange

CORS 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 exchange
  • GET /api/oauth/userinfo — UserInfo endpoint
  • POST /api/oauth/revoke — Token revocation
  • POST /api/oauth/introspect — Token introspection
  • GET /api/oauth/end-session — End session / logout
  • /api/scim/* — SCIM provisioning endpoints
  • GET /.well-known/openid-configuration — OIDC Discovery
  • GET /.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:

HeaderValuePurpose
X-Frame-OptionsDENYPrevents UniAuth from being embedded in iframes, blocking clickjacking attacks
X-Content-Type-OptionsnosniffPrevents browsers from MIME-sniffing responses, reducing drive-by download attacks
Strict-Transport-Securitymax-age=31536000; includeSubDomainsForces browsers to use HTTPS for 1 year, preventing protocol downgrade attacks (production only)
Content-Security-Policyframe-ancestors 'none'; base-uri 'self'; form-action 'self'Prevents framing, restricts form submission and base URI to same origin
Referrer-Policystrict-origin-when-cross-originLimits referrer header to origin only on cross-origin requests, preventing URL leakage
Permissions-Policygeolocation=(), 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=5d41402abc4b2a76b9719d911017c592e0f3c5a7d8b1c2e3f4a5b6c7d8e9f0a1

Always 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:

SignalDescription
New IP addressLogin from an IP address that has never been associated with this account
New device or browserLogin from a previously unseen User-Agent string
Unusual login hoursLogin at a time of day significantly outside the user's normal pattern
Failed attempt burstsMultiple 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 TypeRetention Period
Expired sessions30 days after last activity
Expired tokens (password reset, email verification)7 days after expiration
One-time passwords (OTPs)24 hours after creation
Activity logs1 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-Signature header 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 sub claim 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 state parameter to prevent CSRF attacks during the OAuth flow.
  • Validate ID tokens fully. Always verify the RS256 signature, check iss, aud, exp, and nonce claims 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.