Skip to main content

iam-service — Security Model

Catalog · 07 Security, Compliance & Tenancy · DATA_MODEL · DOMAIN_MODEL · SYNC_CONTRACT

iam-service is the highest-blast-radius service on the platform: a compromise here lets an attacker mint valid JWTs for any tenant. This document defines the cryptography, access controls, audit posture, and incident playbooks.

1. Standards & Conformance

StandardConformance Level
OAuth 2.1 (draft)Full
OIDC 1.0 (Core, Discovery, Dynamic Client Registration where supported)Core, Discovery
WebAuthn Level 2 / FIDO2Full
SAML 2.0 (Web Browser SSO)Full
OWASP ASVS 4.0L2 baseline; auth + sessions at L3
NIST SP 800-63BAAL2 default; AAL3 for platform_admin
ISO 27001 / SOC 2 Type IIMapped controls; reviewed annually
GDPR Art. 25 / 32Data minimization; encryption in transit + at rest
EU AI Act (high-risk)HITL on AI-suggested locks (see AI_INTEGRATION §9)

2. Threat Model (excerpt)

IDThreatSTRIDELikelihoodImpactMitigation
T-01Credential stuffingSpoofingHighHighargon2id; per-IP+per-email lockout; WAF; adaptive MFA.
T-02Refresh-token theft via XSS / malwareSpoofingMediumCriticalRotating refresh; reuse detection → family revoke; short access TTL (15 min).
T-03KMS misconfig → JWT forgeryTamperingLowCatastrophicKMS IAM least-privilege; deploy-time config drift detector; quarterly DR drill.
T-04SAML XSW (XML signature wrapping)TamperingMediumCriticalCertified library; signature → schema → attribute order; clock skew ≤ 5 min; InResponseTo.
T-05OIDC state/nonce replaySpoofingMediumHighState = HMAC(sessionId, tenantId); nonce single-use in Redis.
T-06Account enumeration via resetInformation disclosureMediumMediumConstant-time response; identical 202 envelope; timing tests in CI.
T-07Device fingerprint spoof → offline cert misissueSpoofingLowHighFingerprint = HMAC(tenantSecret, attrs); offline bind requires user re-auth + Ed25519 keypair from device.
T-08API key leak in logs / reposInformation disclosureMediumCriticalHashed (argon2id); only 8-char prefix logged; secret-scan in CI.
T-09Lockout DoSDoSHighMediumIP-scoped lockout when IP is new; magic-link self-recovery; admin-lift endpoint.
T-10JIT-provisioned spam users via SSOSpoofingMediumMediumTenant policy gate on JIT; CAPTCHA on guest registration.
T-11Outbox replay → duplicate eventTamperingLowMediumConsumer dedup by eventId; outbox idempotent.
T-12Breach-list lookup over plaintextInformation disclosureHighHighk-anonymity (HIBP API: only first 5 SHA-1 chars sent).
T-13Session hijack on shared workstationSpoofingMediumHighIdle lock 5 min; device binding; tenant policy can require re-auth on tenant-switch.
T-14Magic-link interception via email forwardingSpoofingLowHigh10-min TTL; single-use; bound to issuing IP/UA optionally; warning template.

Full STRIDE table maintained in security/threat-model.md; reviewed quarterly + on every story touching auth.

3. Authentication Surfaces

SurfaceAccess MethodAALNotes
/api/v1/auth/login (password)passwordAAL1 base; AAL2 with MFAAdaptive challenge promotes to AAL2 on risk.
/api/v1/auth/sso/oidc/*OIDCdepends on IdP acrHonors IdP amr; tenant policy may demand local MFA on top.
/api/v1/auth/sso/saml/*SAML 2.0depends on IdPSame.
/api/v1/auth/webauthn/*WebAuthn (FIDO2)AAL3 with attested authenticatorRequired for platform_admin.
/api/v1/auth/magic-link/*one-time email linkAAL1Guests only by default; tenant can opt-in for staff.
/api/v1/users/me/devices/*/bind-offlinebearer access JWT + acr=fresh-authAAL2 minimum+ tenant CA signature on issued cert.
Internal /internal/*mTLS SPIFFEservice identityNever user-callable.

4. JWT Crypto

AspectChoice
AlgorithmEdDSA (Ed25519)
Signing key locationCloud KMS (HSM-backed); never extractable.
Key hierarchyRoot signing key (per region) → year-monthly kid aliases (e.g. iam-2026-04).
Rotation cadenceMonthly scheduled; emergency on demand.
Rotation overlap≥ 2 days; both kids in JWKS during overlap.
VerificationConsumer pulls JWKS via CDN (5-min TTL); jittered cache.
Token TTLAccess: 900 s · Refresh (online): 30 d · Refresh (offline-bound): 7 d
jtiULID; required claim
Clock skew tolerance60 s

4.1 Emergency rotation

Documented in runbooks/iam/jwt-emergency-rotation.md. Steps: mint new kid → publish JWKS → rotate signing alias → wait 2× cache TTL → revoke compromised kid → mass session revoke (reason='admin_revoke').

5. Credential Storage

ConcernChoice
Algorithmargon2id
Parametersm=64MB, t=3, p=1 (verified annually against OWASP latest)
Salt16-byte random per credential
PepperNone at hash level (defense-in-depth via DB envelope encryption)
Hash bytes envelopeEncrypted at rest via Cloud SQL CMEK + transparent disk encryption
Rehash on loginIf hash_version < current → recompute and update in-place
HistoryLast 5 hashes retained (rotation history check)
Password policyLength ≥ 12, ≥ 3 character classes, no email substring, no breach hit
Breach checkHIBP k-anonymity API on registration + reset; ON every successful login is checked monthly via background job (pwn_audit)

6. Refresh Token Storage

AspectChoice
Token format256-bit opaque, base64url encoded, prefixed rft_
At restSHA-256 hash stored in sessions.current_token_hash; previous N (default 5) in previous_token_hashes
Reuse detectionPresented hash matches a previous → entire family_id revoked, event melmastoon.iam.session.revoked.v1{reason='rotation_reuse'}
TransportAuthorization: Bearer (HTTPS only); HttpOnly cookie permitted as alt for browser clients
Lifetime30 d online; 7 d if did present and offline-binding active
RevocationImmediate on logout / lock / tenant delete / device revoke / password change

7. MFA Crypto

FactorStorageNotes
TOTPSecret envelope-encrypted via KMS DEK; secret_kid recorded for rotationRFC 6238; SHA-1 (default) or SHA-256, 30 s, 6 or 8 digits.
WebAuthncredential_id + public_key + sign_count stored; aaguid for attestation policySign count monotonic — regression triggers clone-detected event.
Recovery codesEach code stored as separate argon2id hash; single-use bit flippedBundle of 10; regenerate invalidates all.
SMSDestination number stored; deprecated for staff/platform; permitted only for guest M0/M1.Out-of-band (notification-service); rate-limited.

8. Device Binding Crypto

AspectChoice
Device keypairEd25519, generated on-device (Electron uses Node crypto.generateKeyPair('ed25519')); private key stored in OS keychain (DPAPI / Keychain / libsecret).
CSRPKCS#10, includes Ed25519 public key, common name did:dev:<DeviceId>, SAN with tenantId + userId.
Cert formatX.509 v3 PEM.
Signing CAPer-tenant intermediate CA in Cloud KMS, signed by Melmastoon platform root CA.
Cert TTL≤ 7 d (tenant-policy bounded).
RevocationDB flag + sync delta (no external CRL/OCSP — tenant CA is private).
RenewalT-24h pre-expiry trigger in Electron; rolls binding atomically.

8.1 Tenant CA bootstrap

Melmastoon Platform Root CA (KMS, ED25519, root key, 30y)


Tenant CA — ten_01HZ8X (KMS, ED25519, intermediate, 5y)


Device Cert — dev_01HZ… (issued by tenant CA, ≤ 7d)

Created at melmastoon.tenant.created.v1 consumption; rotated annually with overlap.

9. API Key Crypto

AspectChoice
Key formatmlk_<26 base32> (issued ULID) + _<random suffix>; never log raw
Storageargon2id hash + 8-char prefix (for log search)
HMAC pepperTenant-specific HMAC pepper key_hash_kid for additional defense
ValidationLook up by prefix → argon2id verify; reject expired/revoked
DenylistRedis iam:apikey:denylist:<prefix> 60-min TTL bridges replication lag after revoke
AspectChoice
Token256-bit random + ULID, format ml_<26 base32>
StorageServer-side: password_reset_requests.token_hash (sha256) + Redis iam:magic:<sha>
TTL10 min
Single-useRedis DEL on redeem; idempotency table catches replay
BindingOptionally bound to issuing IP/UA (tenant policy)

11. RBAC / ABAC Matrix (within iam scope)

iam-service exposes a small authorization surface (most authz is downstream). The matrix below covers iam endpoints only.

RoleEndpointAllowed
anonymous/auth/register, /auth/login, /auth/refresh, /auth/sso/*, /auth/magic-link/*, /auth/password/reset/*, /.well-known/jwks.json
user (any)/users/me/*, /auth/logout, /auth/mfa/*, /auth/devices/*✅ self only
tenant_admin/users/{id}/lock, /users/{id}/unlock✅ within tenant
tenant_admin/users/{id}/devices/*✅ within tenant (read + revoke)
platform_adminAll admin routes✅ platform-wide
guest/auth/devices/{id}/bind-offline, /api-keys❌ (platform-wide policy)
API key (scope=admin:*)/users/{id}/lock✅ if tenant matches key's tenant

ABAC checks: tid == requested.tenantId for tenant-scoped admin actions; userType in {staff, platform_admin} for any privileged surface.

12. Audit Logging

Every security-sensitive action writes one row to iam.audit_events. Append-only enforced by trigger.

ActionWhen
iam.user.registeredAlways
iam.user.login_succeeded, iam.user.login_failedAlways
iam.user.locked, …unlockedAlways
iam.session.refreshed, …revokedAlways
iam.password.reset_requested, …completed, iam.password.changedAlways
iam.mfa.enrolled, …removedAlways
iam.device.registered, …trusted, …bound_for_offline, …revokedAlways
iam.apikey.issued, …revoked, …unauthorized_useAlways
iam.external_identity.linked, …unlinkedAlways
iam.admin.lock, …unlock, …force_logoutAlways; carries actor userId
iam.gdpr.erasure_completedAlways
iam.kms.signing_key_rotatedAlways; carries kid

Audit search is performed by audit-service (consumes events). Direct querying of iam.audit_events is restricted to platform compliance role.

13. Audit Retention

Retention classWindow
regulated (e.g. user.registered, mfa_enrolled, device.bound_for_offline, apikey.issued)7 years
security (failures, locks, revocations)1 year primary + 6 years cold
operational (refreshes, success logins)90 days primary + analytical tier
Audit table partitionsMonthly; archived to BigQuery; tenant-erasure-aware purge job

14. GDPR Participation

RightBehavior
Right of access (Art. 15)iam-service exposes /users/me/snapshot and /admin/users/{id}/snapshot; aggregated by gdpr-service into the DSAR PDF.
Right to rectification (Art. 16)email change is staff-supported; emits …email_changed.v1 (out of M0 scope).
Right to erasure (Art. 17)Saga participant; on melmastoon.tenant.guest.erasure_requested.v1 we anonymize User.primary_email to gdpr-erased-<userId>@example.invalid, delete credentials/sessions/devices/MFA/API-keys/external-identities; emit melmastoon.iam.user.erased.v1. Audit log retained as legal-hold (Recital 65 / Art. 17(3)(b)).
Right to portability (Art. 20)iam-service portion is minimal (login records); merged into export by gdpr-service.
Right to object (Art. 21)n/a for security processing (Art. 6(1)(f)).

15. Network Security

LayerControl
TransportTLS 1.3 only; HSTS max-age=63072000; includeSubDomains; preload.
mTLSInternal mesh (SPIFFE id spiffe://melmastoon/prod/iam-service); enforced by service mesh.
WAFCloud Armor + custom credential-stuffing signatures.
Geo controlsPer-tenant allowlist/denylist (OFAC + tenant config).
Anti-botTurnstile/reCAPTCHA on register + magic-link request.
Rate limits (edge)/auth/login 10/min/IP; /auth/password/reset/request 3/h/email; /auth/refresh 60/min/family.

16. Security Headers (HTTP)

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: same-site
Permissions-Policy: camera=(), microphone=(), geolocation=()
Content-Security-Policy: default-src 'none'; frame-ancestors 'none'
Cache-Control: no-store

17. Secret Management

SecretStorageRotation
JWT signing keyCloud KMS (HSM)Monthly; emergency on demand
Tenant CA rootCloud KMSAnnual
OIDC client secretsSecret ManagerQuarterly or per IdP cadence
SAML signing keyCloud KMSAnnual
HIBP API keySecret ManagerPer provider cadence
SMTP credsSecret ManagerAnnual
Tenant fingerprint HMAC secretCloud KMS DEKAnnual
API-key HMAC pepperCloud KMS DEKAnnual
Magic-link signingKMS-derived ephemeral; no long-lived secretn/a

No secrets in env files committed to the repo. CI uses workload identity federation; local dev uses .env.local (gitignored).

18. Incident Response Playbooks

ScenarioRunbook
Suspected JWT forgeryrunbooks/iam/jwt-forgery.md
KMS outagerunbooks/iam/kms-outage.md
Mass credential stuffingrunbooks/iam/credential-stuffing.md
Refresh-token theft suspectedrunbooks/iam/token-theft.md
SAML metadata driftrunbooks/iam/saml-drift.md
GDPR erasure stuckrunbooks/iam/gdpr-stuck.md
Device-cert mass expiryrunbooks/iam/device-cert-expiry.md
Breach-list provider downrunbooks/iam/hibp-down.md

Each runbook includes: detection, triage, mitigation, comms, recovery, postmortem template.

19. Penetration Testing

CadenceScope
Annual external pen testFull iam surface incl. SSO + WebAuthn
Quarterly internal red teamCredential stuffing, token theft, SSO substitution, side-channel timing
Per-releaseOWASP ZAP automated scan; SAST (Semgrep); SCA (Trivy / Snyk)
Pre-prodDAST against staging

Findings tracked in security/findings/; CRITICAL must be closed pre-release.

20. Backward Compatibility (Crypto)

  • argon2id parameters may be hardened over time; rehash-on-login pattern (see MIGRATION_PLAN §6) keeps users on current params.
  • Ed25519 → future curve migration: new device bindings use new curve; existing bindings remain valid until expiry; bundles encrypted with key derived from existing public key remain decryptable.
  • JWT kid rotation never invalidates a session (only the access token's kid; refresh is opaque).