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 language

Prerequisites

- A working Microsoft Graph integration in any language (C#, PowerShell, Python, Node, Java, Go, ...) - A GraphWarden proxy URL reachable from your app (issued when the proxy is deployed) - Proxy credentials — a Proxy Client ID and Proxy Client Secret issued by the GraphWarden App Admin for the App Identity that represents your app

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 Requests responses with their Retry-After header 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_id and client_secret live 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, and log_only verdicts 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.

  1. Call an endpoint you already know returns data against direct Graph — /v1.0/users?$top=1 is a safe smoke test.
  2. Repeat the call against the proxy URL with the Proxy Client ID and Proxy Client Secret. Expect the same response shape. A filter rule may remove properties; expected keys should still be present.
  3. Open GraphWarden App Admin, find the audit log for your App Identity, and confirm the request is recorded with the matched rule and verdict.
  4. Trigger a known-deny path (for example, a DELETE that a rule blocks) and confirm you receive 403 Forbidden with 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.

If you need code rather than pseudo-code, the .NET guide shows a complete working sample that points a `GraphServiceClient` at the proxy base URL. The PowerShell quickstart covers the same pattern for `Connect-MgGraph`.

Last reviewed:

Last reviewed: .