Trust model: zero-knowledge credentials and what GraphWarden does not see

GraphWarden never holds your Graph API credentials at rest. Those credentials live in YOUR Azure Key Vault, in YOUR tenant. The proxy reads them at runtime through a Managed Identity, obtains a Graph token via MSAL, and forwards the token upstream. Audit logs capture metadata — timestamps, paths, statuses, rule verdicts — not request or response bodies.

Implementation shipped — deep compliance audit scheduled for Phase 5

Prerequisites

- An Azure Key Vault in your tenant with the Graph Client Secret written to a known entry - A Managed Identity (or service principal, for dev) that the proxy runs as - `get` permission for the proxy's identity on the specific Key Vault secret

Where your credentials live

Credential location is the whole foundation of the zero-knowledge claim. If any piece of it is wrong, the property does not hold.

  • Graph Client Secret — stored in your Azure Key Vault (microsoft_tenants.key_vault_url) at a conventioned secret name. GraphWarden reads the value on demand using the proxy's Managed Identity. The value is kept in memory only for the lifetime of the Graph token MSAL issues from it.
  • Proxy Client Secret — stored as a SHA-256 hash on each proxy instance (proxy_client_secret_hash). The cleartext is shown to the App Admin operator ONCE at creation time and then discarded.
  • HMAC secret for the agent API — unique per proxy instance, generated at enrollment, stored encrypted in App Admin. Used to sign proxy-to-AppAdmin heartbeats and sync calls. Not a Graph credential; it never leaves the pair.

GraphWarden itself holds no copy of your Graph Client Secret at rest. An engineer with full access to the GraphWarden App Admin database cannot recover your Graph Client Secret from it.

What GraphWarden sees vs. does not see

The table below lists every field GraphWarden observes about a Graph request. Everything not listed is NOT recorded.

Data class Seen by the proxy in-flight Written to audit store
Request HTTP method Yes Yes
Request path and query string Yes Yes
Caller App Identity (by proxy client_id) Yes Yes
Response HTTP status Yes Yes
Latency in milliseconds Yes Yes
Matched rule ID and action Yes Yes
Correlation ID Yes Yes
Request body Yes (forwarded) No
Response body Yes (forwarded) No (default)
Individual field values in a Graph response Only when a response_filter inspects them to redact No
Azure AD user passwords, tokens, refresh tokens Never Never
Graph Client Secret cleartext Only in memory during MSAL exchange; never persisted Never
The implementation details above (Key Vault access via Managed Identity, secret name convention, in-memory lifetime of the Graph Client Secret) have been verified against the proxy architecture spec. A deeper compliance audit of Key Vault access patterns — covering break-glass scenarios, key rotation policies under Azure Policy, and audit-trail coverage of secret reads — is scheduled for the Phase 5 compliance pages.

Audit logs

The audit store is metadata-only by default. Each Graph request produces one audit event with the fields listed above. Events are shipped to Elasticsearch at graphwarden-audit-{org_uuid}-YYYY.MM when Audit:ElasticsearchUrl is configured, and to a MySQL fallback table for small deployments.

Retention is controlled by microsoft_tenants.audit_retention_tier. Supported tiers: base (30 days), 90d, 1y, 7y. The tier maps to an Elasticsearch ILM policy that enforces deletion automatically. Retention applies only to audit events; it does not affect your Graph data, which remains in your Azure AD tenant under Microsoft's retention terms.

Response filtering

When a rule declares action: filter, the proxy DOES inspect the response body — this is the tradeoff you accept when you ask GraphWarden to redact fields. The inspection is in-memory on the outbound path: the body is parsed, the filter's allow_properties or strip_properties are applied, and the rewritten body is returned to the caller.

The rewritten body is NOT written to the audit store. Only the rule verdict (matched rule ID, whether a filter ran) is recorded. If your threat model requires that GraphWarden never parse response payloads at all, use only allow, block, and log_only rules and leave response filtering to your client application.

Troubleshooting

Proxy log shows KeyVault: access denied — the proxy's Managed Identity does not have get on the specific secret. Azure Key Vault grants are per-secret when RBAC mode is enabled; grant Key Vault Secrets User scoped to the one secret.

Audit events contain request bodies despite the default — a custom Audit:Destination sink was added or the deployment is running with Audit:CaptureBodies=true (a development-only flag). Confirm in appsettings.json and disable for production.

I want to prove GraphWarden never saw a rotated Graph secret — Azure Key Vault access logs record every get call by caller identity and timestamp. Correlate the secret-write event (your rotation) with the first subsequent get (the next MSAL token request). The proxy's in-memory copy is discarded when the Graph token expires.

response_filter is not running but I expected body redaction — only action: filter triggers response inspection. action: allow passes bodies through unchanged, and action: log_only records the event without modifying the response.

Take this to your security committee

The trust-model claims above map directly to customer use cases where a zero-knowledge credential story was the buying signal. To walk the mapping against your own Key Vault topology and Managed Identity layout, contact the GraphWarden team.

Last reviewed:

Last reviewed: .