# 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
