Architecture

12. Threat model

A living, adversarial view of the system: what is worth attacking, where the trust boundaries are, who the adversaries are, and which attacks an identity provider must withstand — each mapped to the decision that mitigates it. The discipline — abuse-case tests, per-milestone and cadenced / event-triggered reviews, dependency-advisory hygiene, parser fuzzing, external review, and incident feedback — is ADR-0027; this section is the model itself. The frameworks are named there: STRIDE classifies (§12.4), OWASP ASVS v5.0 is the verification rubric, and OWASP Threat Dragon maintains the model as code (reserved until code begins). It is v0/evolving and hardens as surfaces are built — a threat model is never "complete."

12.1 Assets

Asset Why it matters
Signing keys Forge any token if stolen; custody-trait-held, HSM/KMS, per-tenant separation (ADR-0003, ADR-0023).
Credentials (passwords, passkeys, TOTP secrets, client secrets) Impersonate a principal; core-resident, never hot-path-read (ADR-0015).
Tokens & sessions Bearer access; short-TTL, status-list-revocable, sender-constrainable (ADR-0005, ADR-0016).
The directory (principals, PII, group membership) Privacy + account takeover; tenant-partitioned, envelope-encrypted at rest (ADR-0011).
Relation tuples (Forseti store) Privilege escalation; the second consistency domain (ADR-0026).
The audit / identity event stream Tamper to hide attacks; append-only, Wyrd-backstopped (ADR-0022).

12.2 Trust boundaries

Boundaries from least- to most-trusted; the diagram is diagrams/trust-boundaries.mermaid.

  • Internet — relying parties, end-users, upstream IdPs. Untrusted.
  • Edge (Vör)least-trusted internal tier: holds only public key material and cached projections (JWKS, status list). The rule-1 hot path.
  • Regional admission (Modgud) — stateful: sessions, login ceremonies, and core writes. More trusted than the edge; the front-channel attack surface lives here.
  • Core — the most-trusted tier: the Wyrd identity core, the Forseti relation store, KeyCustody (HSM/KMS), and the event log.
  • Admin plane — operator surface, authorized by Forseti itself (ADR-0021).
  • The revocation/projection transport (NATS + projection-distribution pulls) — a named boundary of its own, mirroring ADR-0027 §2: the highest-value spoof-or-suppress target inside the perimeter, mTLS/workload-identity authenticated (ADR-0035) and integrity-protected by projection signatures and freshness (ADR-0043).
  • Tenant isolation — a cross-cutting boundary; no cross-tenant strong read on any hot path (rule 3, ADR-0012).

Edge-compromise blast radius (a stated property). An attacker who fully compromises an edge (Vör) node gains no signing keys, no credentials, and no directory — only public material, caches, and one transport credential: the edge's subscribe-only NATS credential (ADR-0035). They can deny service, serve stale (never forged) revocation, or observe the revocation stream — a privacy consideration for a compromised edge — but they cannot publish or suppress on it, and the projections the edge consumes are signature- and freshness-protected (ADR-0043). This bounded blast radius is a security payoff of the constitution's edge/core split (ADR-0027).

12.3 Adversaries

Anonymous internet attacker · malicious or compromised relying party · malicious end-user (privilege escalation) · malicious or compromised tenant administrator · compromised upstream IdP · a co-tenant (isolation breach) · network MITM · compromised edge node · malicious insider / operator · supply-chain attacker.

12.4 STRIDE, per boundary

Edge (Vör) Admission (Modgud) Core / Forseti
Spoofing token forgery → signature + pinned algs (ADR-0010) credential theft, phishing → passkeys, MFA (ADR-0020) mTLS service identity
Tampering cache poisoning → signed projections request tampering → PKCE, signed state append-only log (ADR-0022)
Repudiation login/issuance events the event stream is the audit truth
Information disclosure only public material at edge session/PII handling envelope encryption; tenant partition
Denial of service edge scales horizontally the write-path DoS target → rate limits (ADR-0012) per-tenant op-rate fairness
Elevation of privilege none (no secrets) step-up assurance in token New-Enemy → consistency token (ADR-0004, ADR-0026)

12.5 The identity attack catalog

Attack Mitigation Owned by
Token forgery / alg:none / RS↔HS algorithm confusion verifier pins the algorithm and key, rejects unknown/none; format version checked ADR-0010, ADR-0003
Token theft / replay short-TTL-dominant + status list; DPoP (reserved) sender-constrains ADR-0005, ADR-0014
Refresh-token theft (the one long-lived bearer) rotation with reuse detection — replay of a consumed refresh token revokes the whole family and raises risk ADR-0014, ADR-0031
Authorization-code injection / replay PKCE mandatory for all clients ADR-0014
OAuth/OIDC mix-up / IdP confusion issuer + audience validation, nonce, state ADR-0014, ADR-0019
Open redirect / redirect manipulation / code interception exact redirect-URI matching against a registered allowlist (no wildcards) ADR-0038, ADR-0014
Stolen / leaked client secret confidential-client secret rotation with overlap; verify-only storage; client revocation kills its tokens ADR-0038, prop 0004
CSRF on the authorization endpoint state + same-site session cookies ADR-0014, ADR-0016
SAML signature wrapping (XSW) / XXE / XML attacks confined to Modgud; hardened parser, schema + signature/canonicalization validation ADR-0017
Session fixation / hijacking opaque server-side session, secure cookies, rotate on auth ADR-0016
Incomplete single-logout (IdP session ends, RP session persists) OIDC back-channel / front-channel logout notifies relying parties; short token TTL backstops a missed notification ADR-0016
Phishing WebAuthn / passkeys are origin-bound and phishing-resistant ADR-0020
Federation account-takeover (email collision) explicit, verified account linking — never implicit email match ADR-0019
Privilege escalation / the "New Enemy" problem decision evaluated at-or-after the consistency-token fence (composite across both domains) ADR-0004, ADR-0026, ADR-0006
Cross-tenant isolation breach four boundaries — namespace + crypto + quota + rate (defense-in-depth) ADR-0012
Account enumeration uniform responses + admission rate limits ADR-0009
Credential stuffing / brute force Argon2id, per-tenant rate limits, MFA ADR-0015, ADR-0020
SSRF (JWKS / SCIM / upstream-IdP fetch) egress allowlists; no internal-network fetch ADR-0017, ADR-0018, ADR-0019
Write-path DoS (login-storm) per-tenant rate limits + sharded write path across both domains ADR-0012, ADR-0026
Signing-key compromise KeyCustody (HSM/KMS); rotation; per-tenant key separation bounds blast radius ADR-0003, ADR-0023
Quantum (CRQC) signing-key recovery → token forgery the public JWKS makes this a forge-all event at CRQC-time (not HNDL); defended by signature-suite agility + hybrid migration before a CRQC, paced to NIST ADR-0042, ADR-0027
Supply-chain compromise permissive-only dependency criteria, cargo-deny advisories, DCO ADR-0008
Malicious insider / operator append-only audit stream; audited break-glass; envelope encryption (operator cannot read the directory) ADR-0022, ADR-0021
Account-recovery abuse / reset-flow ATO proven contact control + recovery codes + rate-limited, session-invalidating reset that never bypasses required assurance prop 0003
Account-creation / bot abuse (CIAM) anti-automation + per-tenant creation limits + contact verification prop 0003, prop 0006
SMS toll fraud / pumping (outbound-cost abuse) per-destination/prefix velocity limits + country allow/deny + per-realm send-cost cap; pattern feeds risk pipeline prop 0011, ADR-0031
Secret-at-rest exposure (client / upstream / SCIM secrets) verify-only secrets hashed; usable secrets envelope-encrypted under KeyCustody; no secrets in logs prop 0004
Resource exhaustion / crypto- & parser-amplification input size/depth/entity limits; cost-before-auth rate-limiting; XML entity-expansion disabled prop 0006
Build / release-pipeline compromise signed releases + SLSA provenance + pinned Actions + protected, signed source prop 0005, ADR-0008
SAML assertion replay one-time assertion-ID tracking + NotOnOrAfter enforcement at the SP / Modgud ADR-0017
Disaster / data loss; incoherent cross-domain restore version-stamped per-domain backups + the restore offset invariant (authz never leads identity); projections rebuilt, not restored ADR-0040, ADR-0026
Cross-relying-party user correlation (privacy / linkability) minimal claims; opaque internal id; pairwise subject identifiers (reserved seat) ADR-0041, ADR-0011
Inconsistent freshness budget silently breaking the kill-now guarantee the budget set is validated against its ordering invariants at config load; an inconsistent profile fails closed ADR-0039

12.6 Abuse-case tests

The catalog above is made mechanical (ADR-0027): each defended attack is asserted to fail, seed-reproducible, from the milestone where its surface exists — the security analog of the edge-independence property test (ADR-0010). Representative: a forged alg:none token is rejected (M0); a token presented to the wrong tenant is rejected (M4); a SAML assertion with a wrapped signature is rejected (M6); an authorization decision after a just-revoked grant reflects the revocation when fenced (M3, V9). A defended attack that ever succeeds becomes a permanent regression test. The full testing plan — the tiers, the CI gates, and the threat→test map — is §13 (ADR-0028).

12.7 Residual and accepted risks

  • The bearer-token validity window — a stolen token is valid until expiry / status-list refresh; the deliberate cost of edge-statelessness, bounded by short TTL + DPoP (ADR-0005).
  • Bounded revocation freshness — "kill now" is within a budget, not instantaneous (ADR-0005).
  • Cold-start availability — a cold edge during a core outage fails closed (safe, but a stated availability cost) (ADR-0010). A warm edge's autonomy is bounded the same way: the core-outage survival window is the projection freshness budget, past which it too fails closed (ADR-0032, ADR-0039).

12.8 Gaps and their disposition

The threat model records its own holes — and tracks them to closure. The gaps a posture review surfaced are now dispositioned.

Addressed as proposals (they fit the existing architecture): account recovery & contact verification (0003), secret handling (0004), build/release integrity (0005), resource-exhaustion hardening (0006), and credential policy & breached-password protection (0013).

Decided in (Proposed) ADRs (they affect the architecture):

  • Status-list freshness / replay — the verifier enforces max-age + a monotonic counter (ADR-0032).
  • Clock / time-source trust — bounded skew + authenticated time + implausible-clock fail-closed (ADR-0033).
  • Right-to-erasure — crypto-shredding against the immutable log (ADR-0034).
  • Internal service-to-service trust — mTLS / SPIFFE for Vord's planes (ADR-0035).
  • Key-compromise emergency response — wholesale kid revocation + forced re-issue (ADR-0036).
  • Risk-based / adaptive auth, device trust & token-theft detection (ADR-0031).
  • Consent & scope-grant enforcement (ADR-0037).
  • OAuth client model — registration, confidential-client authentication and secret rotation, and exact-match redirect-URI validation (ADR-0038).
  • The freshness / time-budget system — the coupled budget set, its ordering invariants, and a fail-closed config-load validation gate (ADR-0039).
  • Backup, restore & disaster recovery — version-stamped per-domain backups, the cross-domain restore offset invariant, and rebuilt projections (ADR-0040).
  • Privacy threat model (LINDDUN) & privacy-by-design — the privacy pass parallel to STRIDE, with data-minimization commitments and reserved pairwise subject identifiers (ADR-0041), taking up the pass ADR-0027 §5 reserved.
  • Post-quantum cryptographic posture — signature-suite agility plus a reserved hybrid migration seat, the exposure framed as CRQC-time forge-all (not HNDL) and paced to NIST (ADR-0042).

Still genuinely open: the [OPEN] items inside the ADRs (the CIAM principal extension, ADR-0011; cross-tenant sharing, ADR-0012; org/crate-name availability, ADR-0001/0009), and the free-tooling CI workflow files (CodeQL, OSS-Fuzz, Scorecard), which land with code at M0 (proposal 0005). The privacy pass that was reserved is now taken up in ADR-0041.