App identities: proxy credentials vs. real Graph credentials

An App Identity is GraphWarden's record of a client application that talks to Microsoft Graph through the proxy. It carries two credential pairs: the proxy pair your app uses to authenticate to GraphWarden, and the real Graph Client ID plus tenant secret that GraphWarden itself uses when calling Graph on your behalf. Your app only ever sees the proxy pair.

Shipped — Credential rotation is a supported flow

Prerequisites

- An Azure AD application registration in your tenant (this supplies the Graph App and its real `client_id`) - Access to the customer Azure Key Vault so GraphWarden can read the Graph Client Secret - The GraphWarden App Admin UI (you will create the App Identity there)

The two credential pairs

GraphWarden's zero-knowledge stance turns on this separation. Confusion about which credential does what is the single most common onboarding mistake.

Credential Issued by Held by Used to
Proxy Client ID GraphWarden App Admin Your client application Identify the caller on POST /proxy/token
Proxy Client Secret GraphWarden App Admin Your client application (hashed on the proxy) Authenticate the caller on POST /proxy/token
Graph Client ID Your Azure AD tenant GraphWarden (in App Admin database) Impersonate the real Graph App against Microsoft Graph
Graph Client Secret Your Azure AD tenant Your Azure Key Vault (GraphWarden reads at runtime) Obtain a Graph token via MSAL client-credentials flow

Your app embeds only the proxy pair. If your source code or CI environment contains the Graph Client Secret, you have bypassed GraphWarden entirely and the zero-knowledge property does not hold.

Where credentials live

Each credential has one authoritative home. Copies elsewhere are drift.

  • Proxy Client ID — issued and stored in the GraphWarden App Admin database (app_identities.proxy_client_id). Distributed to your client app by whoever provisions the App Identity. Non-secret.
  • Proxy Client Secret — generated by App Admin at creation time, shown once, then stored only as a SHA-256 hash on each proxy instance (proxy_client_secret_hash). Regenerate by rotating (see below); the old secret is discarded the moment a new one is issued.
  • Graph Client ID — copied from your Azure AD app registration into App Admin (app_identities.real_client_id). Non-secret; Azure AD treats this as a public identifier.
  • Graph Client Secret — NEVER stored in App Admin or the proxy. Written directly to microsoft_tenants.key_vault_url by an operator using the GraphWarden Key Vault helper. The proxy reads it at runtime using Managed Identity (production) or an explicit service principal (development). A secret name of tenant-{tenant_uuid}-identity-{real_client_id} is the convention.

Creating an App Identity

The canonical path is through the GraphWarden App Admin UI. The flow below is the conceptual sequence; the App Admin guide covers the click-by-click steps.

  1. Register the Graph App in Azure AD — create the Azure AD application, grant it the application permissions you want GraphWarden to broker, and obtain the real client_id plus a fresh client secret.
  2. Write the Graph secret to Key Vault — use the Key Vault helper to store the secret at the convention-named secret. GraphWarden never sees the cleartext.
  3. Create the App Identity in App Admin — supply the real_client_id, pick the ruleset to bind, and submit. App Admin generates the proxy_client_id and a fresh proxy_client_secret, shows the secret ONCE, and records only the hash.
  4. Distribute the proxy pair to your application — store the proxy credentials in your app's secret manager (environment variables, CI secrets, Azure Key Vault for your app, etc.). Ship the values via your existing secret-distribution channel; GraphWarden does not re-surface them.

Rotating credentials

Both credential pairs rotate independently.

Proxy pair — in App Admin, open the App Identity and click Rotate Secret. A new secret is generated, the new hash replaces the old one on the next sync cycle (typically seconds), and the old cleartext is destroyed. Update your application's configuration to the new secret before the sync runs to avoid a token-request gap, or accept a brief 401 window.

Graph pair — rotate the Graph Client Secret in Azure AD first, then write the new secret to the same Key Vault entry. GraphWarden picks it up on the next MSAL token request (expired tokens are re-acquired transparently). The real_client_id rarely changes; if it does, update App Admin and re-provision the Key Vault entry under the new convention name.

Rotation is a supported operation for both credential pairs. Automating rotation via a scheduled Azure DevOps or GitHub Actions job is a common pattern; the Key Vault helper exposes a CLI that fits into those pipelines.

Troubleshooting

401 on /proxy/token immediately after a rotation — the proxy instance has not yet resynced the new hash. Wait one sync cycle (RuleSync:IntervalSeconds, default 60 seconds) or hit the admin API to force a sync.

Graph returns 401 AADSTS7000215 through the proxy — the Graph Client Secret in Key Vault is expired or wrong. Confirm the entry exists at tenant-{tenant_uuid}-identity-{real_client_id} and that the value matches the secret shown in Azure AD.

Proxy logs KeyNotFound for a Key Vault secret — the proxy's Managed Identity lacks get permission on that specific secret. Grant get (NOT list or set) on the secret in Key Vault access policies or RBAC.

Two App Identities for the same Graph App — supported, and often useful when one app uses distinct rulesets for distinct workflows (e.g., a read-only reporting job and an admin-write job). Each App Identity carries its own proxy pair; the Graph App can be shared.

Last reviewed:

Last reviewed: .