- 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
147 lines
5.9 KiB
Markdown
147 lines
5.9 KiB
Markdown
# 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
|