# Email Link Delivery Implementation Plan

## Goal
Replace email attachments (PDF + uploaded docs) with secure, link-based delivery that requires authentication first and uses tightly scoped fallback and step-up controls. This reduces PII in inboxes, eliminates large forwardable attachments, and preserves auditability and revocation while keeping the office workflow low-friction enough to drive adoption.

## Audience and Assumptions
- All notification recipients are internal CBM users.
- External recipients are out of scope.
- Existing admin tokenized PDF URLs remain unchanged for the admin UI.
- Sensitive forms are forms with PII or identifying images, starting with `NewHire`.

## Scope
In scope:
- All form notification emails that currently send attachments.
- `NewHire` delivery where the PDF and ID/SSN-related uploads are currently attached.
- Backend token generation, file metadata, download endpoints, OTP step-up, storage confidentiality, and email template updates.

Out of scope:
- Emergency/manual resend scripts.
- Frontend admin-list rendering behavior already handled by existing tokenized admin URLs.
- External recipient flows.

## Decision Summary
- **Primary access:** authenticated portal access for `Admin` and `Office` users.
- **Tokenized URLs:** still used to prevent enumeration, but require auth on click.
- **Fallback bounded token:** internal-domain only, used only if the normal auth path is unavailable.
- **OTP:** not required for normal authenticated standard-form access. Required as step-up for sensitive forms and for fallback verification.
- **Sensitive forms:** mandatory OTP step-up plus lower TTL and download caps.
- **Packaging:** PDF as a standalone link plus uploads-only ZIP for forms with supporting files.

## Access Model

### Auth-first flow
1. User clicks emailed link.
2. If a valid session exists, access proceeds directly.
3. If no session exists, redirect to login and then back to the requested resource.
4. Authorization enforces `Admin` / `Office` role and form visibility rules.
5. Sensitive forms require OTP step-up before file access.

### OTP step-up flow
- OTP is issued after auth for sensitive forms, or after fallback token validation when auth is unavailable.
- OTP is delivered to the recipient's corporate inbox.
- Successful OTP verification grants a short, resource-scoped access window, not a full login session.
- During that grant window, the user can access the same link/package without repeated OTP prompts.

### Why OTP is separate from login
- Login proves identity broadly and establishes a portal session.
- OTP proves possession of the corporate inbox for a single sensitive resource and expires quickly.
- OTP remains useful if the application is up but the normal auth path is unavailable.

## Policy Matrix

| Setting | Standard Forms | Sensitive Forms (`NewHire`) |
|---|---:|---:|
| Link TTL | 24h | 12h |
| Max downloads | 2 | 1 |
| OTP required (authenticated) | No | Yes |
| OTP required (fallback) | Yes | Yes |
| OTP validity | 5 min | 5 min |
| OTP max attempts | 5 | 5 |
| Grant window | 20 min | 15 min |
| Rate limit (token + IP) | 10/min | 5/min |

## Packaging Strategy
- Standard forms: email contains a single PDF link.
- `NewHire` and future upload-bearing forms:
  - one PDF link,
  - one uploads-only ZIP link,
  - optional per-file links on the package page.
- ZIP is generated on demand and streamed rather than stored persistently.

## Data Model

### Token metadata
Store lifecycle metadata for every tokenized email access link:
- `jti`
- `formType`
- `formId`
- `scope`
- `sensitivity`
- `recipientDomain`
- `expiresAt`
- `maxDownloads`
- `downloadCount`
- `revokedAt`

### File asset metadata
Store one row per physical asset:
- `formType`
- `formId`
- `fileCategory`
- `storagePath`
- `originalFileName`
- `contentType`
- `sizeBytes`
- `checksum`

### Encryption metadata
For encrypted assets, store:
- wrapped DEK (`dekWrapped`)
- nonce
- DEK algorithm
- KEK key id
- KEK version
- wrap algorithm
- `encrypted` flag

### Audit trail
Store access attempts for:
- view/download
- OTP issue/verify
- invalid/revoked/exhausted token attempts
- unwrap failures

## Storage Confidentiality
Use envelope encryption for all stored PDFs and sensitive uploads.
- Per-file DEK: AES-256-GCM
- KEK: Azure Key Vault-managed key
- Files on disk are ciphertext only
- Decryption happens only in-memory during streaming
- Old KEK versions remain readable after rotation because each file stores the `kekVersion`
- Revoking/disabling a KEK version intentionally blocks access and acts as a kill switch

## Services
- `EmailLinkService` for token issuance, validation, consumption, and revocation
- `EmailOtpService` for OTP issue, verify, and grant-window checks
- `FormFileAssetService` for registering and reading assets
- `ZipPackageService` for uploads-only ZIP streaming
- `FileEncryptionService` for AES-256-GCM encrypt/decrypt and DEK re-wrap
- `KeyProvider` abstraction with:
  - `LocalKeyProvider` for dev/test
  - `AzureKeyVaultKeyProvider` for stage/prod
- `KekRewrapJob` for rotating wrapped DEKs to the latest KEK version

## API Surface
Authenticated endpoints remain under `/api/v1/admin/forms/...`:
- `GET /{formType}/{formToken}/pdf`
- `GET /{formType}/{formToken}/uploads.zip`
- `GET /{formType}/{formToken}/files/{fileCategory}`
- `POST /{formType}/{formToken}/otp/request`
- `POST /{formType}/{formToken}/otp/verify`

Invalid or tampered tokens should resolve to `404` to avoid probing.

## Configuration
Key config groups:
- `cbm.email.delivery.*`
- `cbm.email.link.*`
- `cbm.email.otp.*`
- `cbm.email.rate-limit.*`
- `cbm.security.encryption.*`

Profiles:
- `dev` / `test`: local KEK provider
- `stage` / `prod`: Azure Key Vault provider

## Rollout Approach
- Phase 1: data model + service scaffolding
- Phase 1.5: storage confidentiality + Key Vault
- Phase 2: asset registration during form processing
- Phase 3: standard forms in `DUAL` mode
- Phase 4: `NewHire` link package + OTP step-up
- Phase 5: cut over to `LINK`
- Phase 6: cleanup and hardening

## Validation Strategy
- Unit tests for token lifecycle, OTP lifecycle, local key wrap/unwrap, encrypt/decrypt round-trip, tamper rejection
- Integration tests for asset registration and encrypted reads
- Health visibility for encryption provider state
- Full regression suite before merging configuration or controller changes

## Operational Docs
- One-time provisioning: `deployment/KEY_VAULT_SETUP.md`
- Day-2 ops and incident handling: `deployment/KEY_VAULT_RUNBOOK.md`
- Delivery phases: `EMAIL_LINK_DELIVERY_PHASED_ROLLOUT.md`

