Skip to main content

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 SECURITY enforced; a separate rls-bypass role (BYPASSRLS) for outbox/expiry workers on a separate pool.

2. Authorization gates (who can call what)

CapabilityGate
Self profile (GET /me, PATCH /me)valid JWT
Self sessions / devices / MFAvalid JWT + session bound to same user
User admin (/users/*)tenant-service permission iam.users:* via policy evaluate
Licensing catalogue writesSUPER_ADMIN role at platform scope
License assignmentsSUPER_ADMIN
License readsSUPER_ADMIN or TENANT_ADMIN within own tenant
Service accountsSUPER_ADMIN
Internal routesip-allow + mTLS (service mesh)

3. Encryption classes

DataAt restIn transitKey management
Password hashes (argon2id)Postgres AES-256 disk + argon2id (not reversible)TLS 1.3argon2id params live in config; argon2 secret pepper from KMS
TOTP seedsKMS-wrapped DEKs per tenant; stored ciphertext onlyTLS 1.3DEK rotation 90 days
WebAuthn public keysPlain (public)TLS 1.3n/a
Refresh token hashesargon2n/a
JWT signing keysKMS (never leave HSM)RS256 keys rotated 90 days; dual-key JWKS overlap
Binding certificatesSigned by tenant-specific CA held in KMSTLS 1.3CA rotation 1 year
API key secret hashesHMAC-SHA-256 with per-tenant pepper from KMSTLS 1.3Pepper rotation quarterly
Events (in-flight)NATS JetStream with TLS + NATS auth; payload may contain IDs onlyNATS creds from secrets manager

4. Audit events

EventWhenConsumerSeverity
identity.user.logged_in.v1every loginaudit-serviceinfo
identity.user.mfa_challenge_failed.v1any MFA failaudit-service + SIEMwarn
identity.user.password_changed.v1change / admin resetaudit-serviceinfo
identity.user.suspended.v1admin actionaudit-service + SIEMwarn
identity.device.bound_for_offline.v1binding issuedaudit-serviceinfo
identity.service_account.revoked.v1revocationaudit-service + SIEMwarn
identity.license.assignment.status_changed.v1license state changeaudit-service + billinginfo

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

RequirementIdentity role
Right to accessReturns identity record on POST /gdpr/subject-export saga step
Right to erasureAnonymises 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 disclosuresaudit-service subscribes to all identity events; 7y retention (HIPAA analogue)
Data portability/gdpr/subject-export returns JSON bundle via communication-service
Breach notificationSecurity incidents flagged automatically; 72h notification workflow owned by platform-admin-service

6. Hardening checklist

ControlStatus
Rate limiting on /auth/*Kong plugin configured per route
Breach-list password checkIntegrated pwned-passwords hash prefix API behind port
CSRFNot applicable — all auth via Bearer JWT; cookies only for refresh flow (SameSite=strict, Secure, HttpOnly)
Session fixationRefresh rotation + jti uniqueness
OAuth state/nonceEnforced on all OIDC flows; rejected replays audit-logged
SAMLEncrypted assertions required; entity IDs allowlisted per tenant
Secret scanningGitHub secret scanning + local trufflehog pre-commit
Dependency SBOMGenerated per build; critical CVEs block release
TLS min version1.3 everywhere; Kong configured
Session cookieSecure; HttpOnly; SameSite=Strict; Path=/api/v1/auth/refresh
Admin endpointsAdditional IP allowlist when tenant policy admin_ip_allowlist present

7. Key rotation schedule

KeyFrequencyStrategy
JWT signing (RS256)90 daysPublish new key 7 days before active; dual-publish in JWKS; remove old after 7d grace
Refresh pepperQuarterlyRe-hash on next rotation; old pepper kept for 30 day verify
TOTP DEK90 daysRe-wrap seeds on rotation
Binding cert CAAnnuallyRe-issue to devices proactively; old CA kept for 30 day verify
API key pepperQuarterlySame 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

ThreatMitigation
Credential stuffingRate limit + breach list + adaptive MFA
Token theft via XSSHttpOnly refresh cookie; access JWT in memory only
Offline device theftShort cert lifetime; remote revoke; re-auth on re-online
Admin account compromiseMandatory WebAuthn for SUPER_ADMIN; break-glass two-man rule
SSO assertion forgerySAML signature + encryption required; OIDC state/nonce
License bypassAll module checks routed through effective-license resolver with event-driven cache invalidation ≤30s
Cross-tenant leakageRLS 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.