Route an existing app through GraphWarden
Switching to GraphWarden changes two things in your code: the Graph base URL and the credentials your app sends. The SDK calls, the scopes, and the response shapes stay identical. This page shows the before/after at the language-agnostic level; the language-specific guides show the exact code for each stack.
Applies to every client languagePrerequisites
Produce a migration plan with an AI assistant
Analyze my existing Microsoft Graph API integration and produce a migration plan to route it through GraphWarden.
Walk through my code and identify:
1. Every location the Graph base URL is referenced
2. Every location Graph credentials are read from
3. Any hardcoded endpoints or scopes that a GraphWarden ruleset must permit
Then produce a before/after summary of the changes needed.
Reference documentation: https://graphwarden.com/llms.txt
Ask me for the path to my repository or paste the relevant code.
Reference: llms.txt
What changes and what doesn't
Two values move from Azure AD + Microsoft Graph to GraphWarden. Everything downstream stays on the Graph side.
| Item | Before (direct to Graph) | After (via GraphWarden) |
|---|---|---|
| Base URL | https://graph.microsoft.com |
https://<proxy-host> (issued by GraphWarden) |
client_id sent with token request |
Your Azure AD app's real client_id |
The Proxy Client ID issued by GraphWarden |
client_secret sent with token request |
Your Azure AD app's real secret | The Proxy Client Secret issued by GraphWarden |
| Token endpoint | https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token |
https://<proxy-host>/proxy/token |
| Scopes | https://graph.microsoft.com/.default |
Same — scopes are still declared against Graph |
| Graph paths | /v1.0/users, /beta/groups, ... |
Identical — paths and query strings pass through byte-for-byte |
| SDK calls | graphClient.Users.GetAsync(...), Get-MgUser, ... |
Identical — the SDK talks to the proxy as though it were Graph |
| Response shapes | Microsoft Graph JSON | Identical, except where a rule's Response Filter transforms a property |
The real Azure AD client_id and client_secret move out of your app entirely. The proxy reads the real Graph credentials from your Azure Key Vault at runtime and uses them to call Graph on your behalf. Your app never sees them again.
The before/after pattern
The sequence below is the same in every language. The names of the methods change per SDK; the shape of the call does not.
1. POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
body: grant_type=client_credentials
client_id={real_client_id}
client_secret={real_client_secret}
scope=https://graph.microsoft.com/.default
-> receives a Graph access_token
2. GET https://graph.microsoft.com/v1.0/users?$select=displayName
Authorization: Bearer {graph_access_token}
-> Microsoft Graph response JSON
1. POST https://<proxy-host>/proxy/token
body: grant_type=client_credentials
client_id={proxy_client_id}
client_secret={proxy_client_secret}
-> receives a proxy JWT (still a Bearer token)
2. GET https://<proxy-host>/v1.0/users?$select=displayName
Authorization: Bearer {proxy_jwt}
-> same Microsoft Graph response JSON, potentially filtered by a rule
The client_credentials grant shape is preserved. The token the proxy returns is a proxy JWT, not a Graph token, and the proxy validates it on every subsequent request. From your app's perspective the token is still an opaque Bearer value — you pass it through the Authorization header the same way you always did.
What your app still owns
GraphWarden covers application-to-Graph traffic only. The responsibilities below stay on your side and do not move to the proxy.
- User sign-in. If your app uses delegated flows (interactive sign-in, MFA, On-Behalf-Of), the user still authenticates to Azure AD directly. MSAL, passport-azure-ad, and equivalent libraries stay in your app.
- Token caching. Cache the proxy JWT the same way you cache a Graph token — one in-memory entry keyed on the token's expiry. Treat a 401 as an eviction signal and request a fresh token.
- Retry and backoff. The proxy forwards Graph's
429 Too Many Requestsresponses with theirRetry-Afterheader intact. Your existing backoff logic keeps working. - Error handling. 4xx from Graph comes back as 4xx from the proxy. Non-Graph failures (proxy auth, ruleset denial, upstream Graph unavailable) are reported with distinct status codes — see the troubleshooting table below.
What the proxy now owns
Four responsibilities move into GraphWarden the moment you switch. Your app stops managing them.
- Graph credentials. The real Azure AD
client_idandclient_secretlive in your Key Vault. The proxy reads them at runtime through a Managed Identity and acquires Graph tokens via MSAL. Your app does not need them in config or environment variables anymore. - Ruleset enforcement. Every Graph request is evaluated against the ruleset bound to your App Identity.
allow,filter,block, andlog_onlyverdicts happen inside the proxy before or after the Graph call. - Audit trail. Each request produces an audit event — method, path, caller, status, rule verdict. Bodies are not captured by default.
- Retention. Audit retention tiers (base / 90 days / 1 year / 7 years) are configured per Microsoft Tenant in GraphWarden App Admin; your app does not need to ship logs anywhere.
Testing the switch
A brief checklist for verifying the migration before you cut production traffic over.
- Call an endpoint you already know returns data against direct Graph —
/v1.0/users?$top=1is a safe smoke test. - Repeat the call against the proxy URL with the Proxy Client ID and Proxy Client Secret. Expect the same response shape. A
filterrule may remove properties; expected keys should still be present. - Open GraphWarden App Admin, find the audit log for your App Identity, and confirm the request is recorded with the matched rule and verdict.
- Trigger a known-deny path (for example, a
DELETEthat a rule blocks) and confirm you receive403 Forbiddenwith a GraphWarden error body.
Troubleshooting
The proxy returns distinct status codes for distinct failure modes. Use the table below to map a response to a cause.
401 Unauthorized on /proxy/token
The Proxy Client ID or Proxy Client Secret sent to /proxy/token does not match the hash stored for your App Identity. Confirm you copied both values from GraphWarden App Admin for the right App Identity and that nothing in your pipeline is trimming the secret.
403 Forbidden on a Graph call
A rule in the ruleset bound to your App Identity returned a block verdict for this request. The response body names the rule id. Review the ruleset in GraphWarden App Admin and check the match pattern, the conditions, and any IP or time gates. The What-If Simulator in App Admin lets you replay the request without touching production Graph.
502 Bad Gateway
Microsoft Graph returned a non-2xx upstream response and the proxy is forwarding it as 502. Check the proxy's health endpoint and Graph's status page. Retry with the same payload after a short backoff.
Response differs from direct Graph
A filter rule is applying a Response Filter — either allow_properties (whitelist) or strip_properties (blacklist). Open the matched rule in App Admin and inspect the transform list. The audit log entry for the request names the rule and the transforms applied.
Last reviewed: