Identity Service — Security Model
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 13 Security & Tenancy · 14 Extended Compliance
1. Trust boundaries
- Public endpoints: only
/api/v1/auth/*,/api/v1/me/*,/.well-known/*,/api/v1/users(admin-scoped). - Internal endpoints (
/internal/*): Kong ip-restriction to cluster CIDRs; no JWT. - Postgres: per-service account with
ROW LEVEL SECURITYenforced; a separaterls-bypassrole (BYPASSRLS) for outbox/expiry workers on a separate pool.
2. Authorization gates (who can call what)
| Capability | Gate |
|---|---|
Self profile (GET /me, PATCH /me) | valid JWT |
| Self sessions / devices / MFA | valid JWT + session bound to same user |
User admin (/users/*) | tenant-service permission iam.users:* via policy evaluate |
| Licensing catalogue writes | SUPER_ADMIN role at platform scope |
| License assignments | SUPER_ADMIN |
| License reads | SUPER_ADMIN or TENANT_ADMIN within own tenant |
| Service accounts | SUPER_ADMIN |
| Internal routes | ip-allow + mTLS (service mesh) |
3. Encryption classes
| Data | At rest | In transit | Key management |
|---|---|---|---|
| Password hashes (argon2id) | Postgres AES-256 disk + argon2id (not reversible) | TLS 1.3 | argon2id params live in config; argon2 secret pepper from KMS |
| TOTP seeds | KMS-wrapped DEKs per tenant; stored ciphertext only | TLS 1.3 | DEK rotation 90 days |
| WebAuthn public keys | Plain (public) | TLS 1.3 | n/a |
| Refresh token hashes | argon2 | — | n/a |
| JWT signing keys | KMS (never leave HSM) | — | RS256 keys rotated 90 days; dual-key JWKS overlap |
| Binding certificates | Signed by tenant-specific CA held in KMS | TLS 1.3 | CA rotation 1 year |
| API key secret hashes | HMAC-SHA-256 with per-tenant pepper from KMS | TLS 1.3 | Pepper rotation quarterly |
| Events (in-flight) | NATS JetStream with TLS + NATS auth; payload may contain IDs only | — | NATS creds from secrets manager |
4. Audit events
| Event | When | Consumer | Severity |
|---|---|---|---|
identity.user.logged_in.v1 | every login | audit-service | info |
identity.user.mfa_challenge_failed.v1 | any MFA fail | audit-service + SIEM | warn |
identity.user.password_changed.v1 | change / admin reset | audit-service | info |
identity.user.suspended.v1 | admin action | audit-service + SIEM | warn |
identity.device.bound_for_offline.v1 | binding issued | audit-service | info |
identity.service_account.revoked.v1 | revocation | audit-service + SIEM | warn |
identity.license.assignment.status_changed.v1 | license state change | audit-service + billing | info |
Additional: internal security.incident records are written for refresh replay detection (IDENT_REFRESH_REPLAY), cross-tenant attempts, and circuit-open federation events.
5. GDPR / Afghanistan MoPH compliance participation
| Requirement | Identity role |
|---|---|
| Right to access | Returns identity record on POST /gdpr/subject-export saga step |
| Right to erasure | Anonymises email, first_name, last_name, revokes sessions, deactivates user; keeps pseudonymous userId linkage in license_assignment_history for 7y audit |
| Data residency (AFG MoPH) | Afghan tenants pinned to af-kabul-1 region; no identity data leaves jurisdiction. Federated identity upstreams (if external) chosen from residency-approved list. |
| Accounting of disclosures | audit-service subscribes to all identity events; 7y retention (HIPAA analogue) |
| Data portability | /gdpr/subject-export returns JSON bundle via communication-service |
| Breach notification | Security incidents flagged automatically; 72h notification workflow owned by platform-admin-service |
6. Hardening checklist
| Control | Status |
|---|---|
Rate limiting on /auth/* | Kong plugin configured per route |
| Breach-list password check | Integrated pwned-passwords hash prefix API behind port |
| CSRF | Not applicable — all auth via Bearer JWT; cookies only for refresh flow (SameSite=strict, Secure, HttpOnly) |
| Session fixation | Refresh rotation + jti uniqueness |
| OAuth state/nonce | Enforced on all OIDC flows; rejected replays audit-logged |
| SAML | Encrypted assertions required; entity IDs allowlisted per tenant |
| Secret scanning | GitHub secret scanning + local trufflehog pre-commit |
| Dependency SBOM | Generated per build; critical CVEs block release |
| TLS min version | 1.3 everywhere; Kong configured |
| Session cookie | Secure; HttpOnly; SameSite=Strict; Path=/api/v1/auth/refresh |
| Admin endpoints | Additional IP allowlist when tenant policy admin_ip_allowlist present |
7. Key rotation schedule
| Key | Frequency | Strategy |
|---|---|---|
| JWT signing (RS256) | 90 days | Publish new key 7 days before active; dual-publish in JWKS; remove old after 7d grace |
| Refresh pepper | Quarterly | Re-hash on next rotation; old pepper kept for 30 day verify |
| TOTP DEK | 90 days | Re-wrap seeds on rotation |
| Binding cert CA | Annually | Re-issue to devices proactively; old CA kept for 30 day verify |
| API key pepper | Quarterly | Same as refresh |
8. Break-glass (S4 design)
A break_glass_login endpoint returns a short-lived session tagged amr: ["break_glass"]. Every such session emits identity.user.logged_in.v1 with breakGlass=true and requires:
- A signed reason envelope countersigned by a second Super Admin (two-man rule) OR a tenant-configured break-glass override phrase.
- An auditor attestation within 24h or the session is retroactively marked
suspicious.
9. Threat model highlights
| Threat | Mitigation |
|---|---|
| Credential stuffing | Rate limit + breach list + adaptive MFA |
| Token theft via XSS | HttpOnly refresh cookie; access JWT in memory only |
| Offline device theft | Short cert lifetime; remote revoke; re-auth on re-online |
| Admin account compromise | Mandatory WebAuthn for SUPER_ADMIN; break-glass two-man rule |
| SSO assertion forgery | SAML signature + encryption required; OIDC state/nonce |
| License bypass | All module checks routed through effective-license resolver with event-driven cache invalidation ≤30s |
| Cross-tenant leakage | RLS enforced; integration test tenant-isolation.spec.ts runs in CI |
10. Open questions
- Platform policy for mandatory WebAuthn for clinical roles by M3?
- Confidential computing / Nitro enclaves for argon2id verification to resist host compromise — cost/benefit study in M2.