pediatric-ai-scribe-v3/docs/authentication.md
Daniel a36235c646 v6.1: Turnstile bot protection, LiteLLM provider, PPTX tables, audio backup fixes, docs
- Add Cloudflare Turnstile to login, register, and password reset forms
- Switch AI provider to LiteLLM, transcription to OpenAI Whisper
- Change domain to scribe.pedshub.com
- Fix PPTX export: add tables, bold/italic, numbered lists, code blocks, blockquotes
- Fix announcement banner close button (CSP was blocking inline onclick)
- Fix auth middleware: empty Bearer token now falls through to cookie auth
- Fix audio backups: only save on transcription failure, stop auto-deleting on success
- Soften AI correction injection to prevent model hallucination from correction history
- Fix LiteLLM TTS model name handling (no incorrect openai/ prefix)
- Expand AI instructions textarea in Learning Hub CMS
- Update README for v6 with all features and providers
- Add comprehensive docs/: architecture, API reference, database schema,
  authentication, AI providers, speech, learning hub, configuration, deployment
2026-04-04 22:56:24 +02:00

5.9 KiB

Authentication and Security

This document covers the complete authentication, authorization, and security system for the Pediatric AI Scribe application.


Authentication Methods

Local Authentication

  • Passwords are hashed using bcryptjs with 12 salt rounds.
  • On successful login, a JSON Web Token (JWT) is issued with a 7-day expiry.
  • The token is stored in an httpOnly cookie named ped_auth.
    • In production: secure: true, sameSite: lax.
    • In development: secure: false, sameSite: lax.

Auth Middleware

The authentication middleware checks credentials in the following order:

  1. Looks for a Bearer token in the Authorization header.
  2. If the token is empty or missing (including the case where the header is literally "Bearer " with no token), falls back to reading the ped_auth cookie.

This two-step approach was specifically fixed to handle empty Bearer strings gracefully, preventing false authentication failures from clients that send the header with no value.

TOTP Two-Factor Authentication (2FA)

  • Implemented using the speakeasy library.
  • Setup flow: server generates a TOTP secret, encodes it as a QR code, and the user scans it with an authenticator app.
  • Verification: 6-digit code, with window=1 (accepts codes from the previous and next 30-second interval in addition to the current one).

OIDC / SSO (Single Sign-On)

  • Implements the Authorization Code + PKCE flow using the openid-client library.
  • State parameters are stored in-memory with a 5-minute TTL to prevent replay attacks.
  • On first login via OIDC, a local user account is auto-created using claims from the identity provider.
  • Supported providers:
    • Azure AD
    • Okta
    • Keycloak
    • PocketID
    • Google

Email Verification

  • A 32-byte random hex token is generated and sent to the user's email address.
  • The token expires after 24 hours.

Password Reset

  • A 32-byte random hex token is generated and sent to the user's email address.
  • The token expires after 1 hour.

Cloudflare Turnstile (CAPTCHA)

Turnstile is applied to the following routes:

  • User registration
  • User login
  • Password reset request

Frontend

  • The cf-turnstile widget is rendered with the configured site key.
  • The form validates that the Turnstile challenge was completed before allowing submission.
  • On failure, the widget resets so the user can retry.

Backend

  • The server sends a POST request to https://challenges.cloudflare.com/turnstile/v0/siteverify with the secret key and the client-provided token.
  • Turnstile is only enforced when TURNSTILE_SECRET_KEY is set in the environment. If the variable is absent, the check is skipped entirely. This allows development and self-hosted environments to run without Cloudflare integration.

Rate Limiting

Rate limiting is implemented using express-rate-limit with the following windows:

Endpoint Limit Window
/api/* (general) 60 requests 1 minute
/api/auth/login 10 requests 15 minutes
/api/auth/register 5 requests 1 hour
/api/auth/forgot-password 5 requests 1 hour
/api/auth/resend-verification 3 requests 15 minutes

Content Security Policy (Helmet)

The application uses Helmet to set HTTP security headers. The Content Security Policy directives are configured as follows:

Directive Values
script-src 'self', 'wasm-unsafe-eval', 'unsafe-eval', cdn.jsdelivr.net, challenges.cloudflare.com
script-src-attr 'none' (blocks inline event handlers like onclick)
style-src 'self', 'unsafe-inline', fonts.googleapis.com, cdnjs.cloudflare.com
frame-src 'self', challenges.cloudflare.com
connect-src 'self' + CDN domains + HuggingFace + Cloudflare
object-src 'none'

CORS

  • Production (when APP_URL is set): restricts the allowed origin to the value of APP_URL.
  • Development (when APP_URL is not set): allows all origins.
  • Requests with no origin (such as those from mobile apps or curl) are always permitted.
  • credentials: true is set to allow cookies to be sent cross-origin.

Roles and Authorization

The application defines three user roles:

Role Access Level
admin Full access to all features, including the Admin Panel. The first registered user is automatically promoted to admin.
moderator Standard user access plus Learning Hub CMS management.
user Standard access to patient encounters and AI features.

Audit Logging

All authentication-related events are recorded in the audit_log database table.

Fields

Column Description
user_id The ID of the user involved (null for failed attempts by unknown users)
action The type of event (see below)
ip_address The client IP address
details A JSON object with additional context

Tracked Actions

  • register -- new account created
  • login -- successful login
  • login_failed -- incorrect credentials
  • login_blocked -- blocked by rate limiter or other policy
  • email_verified -- user confirmed their email address
  • Additional actions for password resets, 2FA changes, and OIDC logins