LEARN · DEBUGGING GUIDE

Azure AD Authentication Token Error — A Practical Debugging Guide

Azure AD token errors often stem from mismatched app registrations, expired secrets, or incorrect scopes. This guide shows you exactly how to trace and fix them.

IntermediateAuth8 min read

What this usually means

These errors indicate a mismatch between what Azure AD expects and what your application presents during authentication. The root cause is almost always in the app registration configuration: the client ID, tenant ID, redirect URI, API permissions, or client secret/ certificate. Token expiry is less common than misconfiguration. The AADSTS error code itself tells you the category — missing app, audience mismatch, consent missing — but the underlying fix requires checking the exact registration in the Azure portal against your code's configuration.

( 01 )Fast diagnosis

The first ten minutes — establish facts before touching code.

  • 1Decode the JWT access token on jwt.ms to inspect the 'aud', 'iss', 'appid', and 'scp' claims.
  • 2Run `curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=...&scope=...&client_secret=...&grant_type=client_credentials" "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token"` to reproduce the error manually.
  • 3In the Azure portal, navigate to App registrations > Your app > Certificates & secrets. Check if the client secret has expired — look at the 'Expires' column.
  • 4Under API permissions, verify that the required permissions are granted (green check) and not just added. Look for 'Not granted' warnings.
  • 5Compare the 'aud' claim in the decoded token with the Application ID URI of your API app registration (under Expose an API).
  • 6Check your application logs for the full AADSTS error message — it often includes a Microsoft documentation link specific to the error code.
( 02 )Where to look

The specific files, logs, configs, and dashboards that usually own this bug.

  • searchAzure Portal > App registrations > (your app) > Overview — note the Application (client) ID and Directory (tenant) ID.
  • searchAzure Portal > App registrations > (your app) > Certificates & secrets — check expiry dates.
  • searchAzure Portal > App registrations > (your app) > API permissions — verify granted status and consent.
  • searchAzure Portal > App registrations > (your API) > Expose an API — the Application ID URI must match the token's 'aud'.
  • searchYour application config file (appsettings.json, .env, config.yaml) — check client_id, tenant_id, scope, client_secret.
  • searchNetwork logs or tools like Fiddler/Charles to capture the exact error response body from the /token endpoint.
  • searchAzure AD sign-in logs (Azure Monitor > Sign-in logs) for interactive flows — look for Status = 'Failure' and the error code.
( 03 )Common root causes

Practical causes, not theory. These are the things you will actually find.

  • warningClient secret expired — secrets older than 2 years (or custom max age) cause silent failures.
  • warningWrong tenant ID — using 'common' or 'organizations' instead of a specific tenant when the app expects a single tenant.
  • warningScope mismatch — requesting 'https://graph.microsoft.com/.default' when the app has delegated permissions, or vice versa.
  • warningNo admin consent — for delegated permissions requiring admin consent, the token request returns AADSTS65001.
  • warningAudience mismatch — the token's 'aud' (audience) doesn't match the API's Application ID URI. Often due to using client ID instead of the URI.
  • warningRedirect URI mismatch — in OAuth flows, the redirect URI in the code must exactly match (including trailing slash) the one registered.
( 04 )Fix patterns

Concrete fix directions. Pick the one that matches your root cause.

  • buildRotate client secret: generate a new secret in Certificates & secrets, update your application config, and redeploy.
  • buildConsent to permissions: as a tenant admin, use the admin consent endpoint (`/adminconsent`) or grant consent via the API permissions blade.
  • buildFix audience: ensure your token request uses the correct resource/scope. For custom APIs, use the Application ID URI (e.g., `api://myapp`) as the scope.
  • buildUpdate scope: for client credentials flow, always use `/.default` scope. For delegated flows, use specific scopes like `User.Read`.
  • buildCorrect tenant: replace 'common' with the actual tenant ID (GUID) in the token endpoint URL if your app is single-tenant.
  • buildValidate redirect URIs: in the Azure portal, under Authentication, ensure the redirect URI matches your application's callback URL exactly (including protocol, port, and trailing slash).
( 05 )How to verify

A fix you cannot prove is a guess. Close the loop.

  • verifiedObtain a new token using the fixed configuration and decode it on jwt.ms — confirm 'aud', 'iss', and 'scp' are correct.
  • verifiedMake a test API call with the new token and verify a 200 OK instead of 401.
  • verifiedSet up a token refresh test: wait until the token expires (usually 1 hour) and confirm silent renewal succeeds.
  • verifiedCheck Azure AD sign-in logs for successful token issuance (Status = 'Success', error code = 0).
  • verifiedRun a curl command against the /token endpoint with your fixed parameters and assert the response contains 'access_token'.
( 06 )Mistakes to avoid

Things that make this bug worse or harder to find.

  • warningHardcoding client secrets in source code — use Azure Key Vault or environment variables.
  • warningUsing the same client secret in multiple environments — always use per-environment secrets.
  • warningAssuming token expiry is always the problem — check the AADSTS error code first.
  • warningNeglecting admin consent for production apps — test with a non-admin account to catch consent issues early.
  • warningCopying client ID instead of Application ID URI for the audience scope.
  • warningForgetting that token claims are case-sensitive — e.g., 'scp' vs 'roles' for delegated vs app-only tokens.
( 07 )War story

Production Outage: AADSTS700016 After Secret Rotation

Senior Backend EngineerC# .NET 6, Azure Functions, Microsoft Authentication Library (MSAL), Azure AD B2C

Timeline

  1. 09:15PagerDuty alert: Production API returning 401 for all requests from a microservice.
  2. 09:18Checked Azure AD sign-in logs — error AADSTS700016: Application with identifier 'xxx' was not found.
  3. 09:22Noticed the client ID in the error matched our microservice's client ID. Checked app registration — still exists.
  4. 09:30Compared config in Azure Functions App Settings vs the app registration. Found tenant ID mismatch: one used 'common', the other actual tenant GUID.
  5. 09:35Discovered that yesterday a colleague rotated the client secret but also inadvertently changed the tenant to 'common' in the config file.
  6. 09:40Reverted tenant ID to the correct GUID, updated the client secret, and restarted the function app.
  7. 09:45Verified: API calls returned 200. Alert resolved.

The alarm went off at 9:15 AM — a sudden spike in 401 errors from our order-processing microservice. The error message was AADSTS700016, which usually means the app registration doesn't exist. I immediately checked the Azure portal; the app registration was there. Puzzling.

I decoded a recent token from our logs — the 'aud' and 'iss' looked normal. Then I noticed the error referenced a client ID that matched our app, but the tenant ID in the error was the one we used. I compared the function app's configuration: someone had set the tenant to 'common' instead of our tenant GUID. That was the smoking gun.

Turns out, during a secret rotation the day before, a colleague had updated the config but accidentally changed the tenant ID field. Since 'common' is multi-tenant, Azure AD looked for the app in the 'common' tenant's directory — and didn't find it because our app was single-tenant. We fixed the tenant ID, updated the secret, and restarted. The fix took 30 minutes, but the root cause was a simple config drift.

Root cause

Misconfigured tenant ID in application settings — 'common' instead of the specific tenant GUID — combined with a client secret rotation that omitted consent renewal.

The fix

Reverted tenant ID to the correct GUID, updated the client secret in both Azure Key Vault and app settings, and validated the token endpoint URL.

The lesson

Always double-check configuration changes after secret rotations. Use infrastructure-as-code to enforce tenant ID and other immutable settings.

( 08 )Understanding AADSTS Error Codes

Azure AD returns error codes prefixed with AADSTS. Each code maps to a specific failure category. AADSTS700016 means the application identifier (client ID) was not found in the directory. This happens when the tenant is wrong or the app registration is deleted. AADSTS500208 indicates the token's audience doesn't match the resource's expected audience — often due to using the client ID instead of the Application ID URI. AADSTS65001 means consent is missing: the user or admin hasn't granted the required permissions.

Always capture the full error message. Microsoft's documentation (docs.microsoft.com/azure/active-directory/develop/reference-aadsts-error-codes) lists each code with a description. However, the error message itself often includes the exact mismatch, like 'The application 'xxx' was not found in the directory 'yyy'. That 'yyy' is the tenant ID your request used — compare it to your actual tenant.

( 09 )Token Endpoint Request Anatomy

The OAuth 2.0 token endpoint for Azure AD is: POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token. The {tenant} can be a GUID, 'common', 'organizations', or 'consumers'. For single-tenant apps, you must use the specific tenant GUID. Using 'common' allows any Microsoft account, but if your app registration is scoped to one tenant, it will fail with AADSTS700016.

The request body must include: client_id, scope, client_secret or client_assertion, and grant_type. For client credentials, scope should be 'https://graph.microsoft.com/.default' or your API's Application ID URI + '/.default'. For delegated flows, use specific scopes like 'User.Read'. A common mistake is mixing these — using delegated scopes with client credentials flow yields AADSTS65001.

( 10 )Diagnosing with jwt.ms and Curl

When you get a token, decode it at jwt.ms. Focus on three claims: 'aud' (audience) — should match the API's Application ID URI or Microsoft Graph resource; 'iss' (issuer) — should be 'https://sts.windows.net/{tenant_id}/'; 'appid' — the client ID that requested the token. If 'aud' is your client ID instead of the API URI, the API will reject the token.

Reproduce the token request with curl. Example: curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d "client_id=my-client-id&scope=https://myapi.com/.default&client_secret=my-secret&grant_type=client_credentials" "https://login.microsoftonline.com/my-tenant-id/oauth2/v2.0/token". If you get an error, the response body contains the AADSTS code. This isolates the problem from your application code.

( 12 )Secret Rotation Pitfalls

Client secrets have an expiry date (max 2 years, configurable). When a secret expires, token requests fail with AADSTS7000222 (invalid client secret). But more insidious: if you rotate the secret and update it in your app but forget to update it in all places (e.g., Kubernetes secrets, Key Vault, config files), you'll get intermittent failures.

Best practice: use certificates instead of secrets for production. Certificates can be rotated with zero downtime using Key Vault and managed identities. If you must use secrets, set up monitoring for expiry — Azure AD can send alerts via Azure Monitor. Also, use two secrets (old and new) during rotation to avoid downtime.

Frequently asked questions

What does AADSTS700016 mean and how do I fix it?

AADSTS700016 means Azure AD couldn't find the application with the given client ID in the directory (tenant). Fix: ensure you're using the correct tenant ID in your token endpoint URL. If your app is single-tenant, use the tenant GUID. Also verify the app registration exists and is not deleted. Finally, check that the client ID in your code matches the one in the portal.

Why do I get AADSTS500208 even though my token seems valid?

AADSTS500208 indicates an audience mismatch. The token's 'aud' claim doesn't match the expected audience of the API you're calling. For example, if you requested a token for Microsoft Graph but your code sends it to your custom API, you'll see this error. Fix: ensure the 'scope' parameter matches the resource you're calling. For custom APIs, use the Application ID URI (e.g., api://myapp) instead of the client ID.

How do I fix 'AADSTS65001: The user or administrator has not consented to use the application'?

This error occurs when the application requires delegated permissions that haven't been consented. If the permissions need admin consent, an admin must go to the Azure portal > App registrations > Your app > API permissions and click 'Grant admin consent for {tenant}'. If it's user consent, ensure your app registration allows user consent (Authentication > 'Allow public client flows' and 'Supported account types' includes the user's account). You can also use the admin consent endpoint programmatically.

Can token expiry cause these errors?

Token expiry causes a 401 error but not an AADSTS code — the API returns 'Token expired' or similar. The AADSTS errors (like 700016, 500208, 65001) are from Azure AD during token acquisition, not from the API. So if you see an AADSTS error, the problem is during authentication, not authorization. Token expiry is a separate issue that your code should handle by refreshing the token silently.

Should I use 'common' tenant or a specific tenant ID?

Use 'common' only if your application is multi-tenant and you want to allow users from any Azure AD tenant or Microsoft account. For single-tenant apps (most enterprise apps), use the specific tenant GUID. Using 'common' with a single-tenant app registration will cause AADSTS700016 because the app isn't found in the 'common' tenant's directory. Always match the tenant endpoint to your app registration's supported account types.