Authentication factors
Password
12-character minimum. Complexity validator requires upper, lower, digit, and special character. Checked against breached-password datasets.
TOTP (Authenticator app)
RFC 6238 time-based one-time passwords. Works with Authy, Google Authenticator, 1Password, Bitwarden, etc.
Passkeys (WebAuthn)
FIDO2 phishing-resistant authentication. Platform authenticators (Touch ID, Windows Hello) and roaming authenticators (YubiKey, Titan) both supported.
Google OAuth 2.1
SSO via Google Workspace. Enforced 2-factor at the IdP counts toward our 2FA requirement.
Magic-link sign-in is also available for recovery and low-friction flows, with short TTLs and single-use tokens. Magic links never substitute for 2FA on PHI-accessing accounts.
Session handling
- httpOnly Secure cookie — JWT delivered as
HttpOnly; Secure; SameSite=Lax. Not readable by JavaScript. - No Bearer tokens in client-side storage (
localStorage/sessionStorage) — session state is cookie-only. - Payload TTL — JWT payload expires in 4 hours; refresh is automatic if the user is active.
- Cookie TTL — 1 day, extending on activity.
- Idle timeout — sessions terminate after 30 minutes of inactivity.
- Signout — server-side revocation plus cookie clear; no stale token can survive logout.
Brute-force and enumeration resistance
- Account lockout after 5 failed password attempts. Auto-unlock after 1 hour. Lockouts are logged to the security audit log.
- Rack::Attack rate limiting on every sensitive endpoint, backed by Memorystore Redis for cross-instance consistency:
| Endpoint class | Limit |
|---|---|
| General requests | 300 / 5 min per IP |
| Auth (login, password reset) | 5 / 20 sec |
| 2FA setup | 5 / 1 min |
| Passkey operations | 10 / 1 min |
| Email sending (per target address) | 3 / 5 min |
| Token verification (magic links) | 10 / 1 min |
- Timing-safe comparisons for all password and token checks.
- Indistinguishable responses — invalid-email and wrong-password return the same generic error and the same latency, to prevent enumeration.
- Cloud Armor adds a second layer at the load balancer: 500–1000 req/min per IP with a 5-minute ban on breach, plus OWASP CRS v3.3.
Authorization
Every API action is gated by object-level authorization via Pundit. No request escapes without an explicit policy decision.
- Global enforcement —
ApplicationControllersetsafter_action :verify_authorized. Any controller action that doesn’t callauthorizeor explicitlyskip_authorizationraises in development and fails in production. - 48 policy files cover every PHI-bearing model and every admin action.
- Role-based authorization layered on top:
| Role | Description | PHI access |
|---|---|---|
user | Standard user | Own data only |
analyst | Reporting / analytics | Aggregated or anonymized only |
support | Customer support | Read-only PHI access, scoped to assigned org |
admin | Org administrator | Full access within the org |
super_admin | Denialbase internal | Full platform access; actions audit-logged |
Delegation and API access
- Human-to-system delegation (scoped task tokens, currently in design) — future capability for letting staff authorize Denialbase to take a specific action on their behalf.
- API tokens for server-to-server integrations have narrow scopes and short TTLs.
- All authentication and authorization decisions are recorded in the security audit log.
Identity providers
- Google OAuth 2.1 — live.
- SAML 2.0 SSO (Okta, Azure AD, Google, any SAML IdP) — see SSO setup.
- SCIM provisioning — planned for Q3 2026.