{
  "schemaVersion": "1.0",
  "code": "700212",
  "name": null,
  "title": "AADSTS700212: Federated credential audience doesn't match (fix)",
  "url": "https://brambrinkmeier.com/entra/aadsts/700212/",
  "alternates": {
    "markdown": "https://brambrinkmeier.com/entra/aadsts/700212.md",
    "json": "https://brambrinkmeier.com/api/aadsts/700212.json",
    "html": "https://brambrinkmeier.com/entra/aadsts/700212/"
  },
  "description": "AADSTS700212: the external token's audience matches no federated identity credential. Usually not api://AzureADTokenExchange. Fix and KQL.",
  "summary": "AADSTS700212 means a workload sent an external token whose audience (aud claim) matches none of the federated identity credentials on the app or managed identity. Usually the token was requested for the wrong audience, for example a Kubernetes token not projected for api://AzureADTokenExchange. It is a configuration error with low attack value.",
  "inMicrosoftReference": false,
  "unlistedChecked": "2026-10-30",
  "meaningText": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS700212 (checked 30 October 2026). We found no other Microsoft page that names it either, so the wording below comes from user reports. A user on Microsoft Q&A, running a workload on Azure Kubernetes Service, posted this response (cluster values shortened):\n\n> AADSTS700212: No matching federated identity record found for presented assertion audience '`https://uaenorth.oic.prod-aks.azure.com/<tenant>/<guid>/,https://<cluster>.hcp.uaenorth.azmk8s.io,...`'. Please note that the matching is done using a case-sensitive comparison. Please check your federated identity credential Subject, Audience and Issuer against the presented assertion.\n\nThe response had `\"error\":\"invalid_client\"`. The audience the user's token carried was the cluster's own URLs, not `api://AzureADTokenExchange`.\n\nWhat Microsoft does document is the audience rule. A federated identity credential lists the audience Entra accepts. Microsoft's Graph overview: it *\"says what Microsoft identity platform should accept in the `aud` claim in the incoming token\"* and *\"should be set to `api://AzureADTokenExchange` for Microsoft Entra ID\"*. The restrictions page: *\"You must add a single audience value\"*. Matching is case-sensitive.\n\n700212 looks like the audience-specific form of [70021](https://brambrinkmeier.com/entra/aadsts/70021/); Microsoft doesn't say so. Its siblings are [700211](https://brambrinkmeier.com/entra/aadsts/700211/) (issuer) and [700213](https://brambrinkmeier.com/entra/aadsts/700213/) (subject); [70025](https://brambrinkmeier.com/entra/aadsts/70025/) means the app has no federated credentials at all.\n\n**A lab test on 30 October 2026 returned a different code for this case.** With a federated credential whose audience didn't match the presented token, my lab tenant answered with [`AADSTS7002121`](https://brambrinkmeier.com/entra/aadsts/7002121/), not 700212:\n\n> AADSTS7002121: No matching federated identity record found for presented assertion audience '<audience>'. The Audience for the federated identity credential...\n\nIn the same run, a wrong issuer gave [700211](https://brambrinkmeier.com/entra/aadsts/700211/) and a wrong subject gave [700213](https://brambrinkmeier.com/entra/aadsts/700213/), so those two are confirmed. For the audience, 7002121 is what Entra returns today on a current tenant. We can't tell from one tenant whether 700212 is an older code, a different-cloud code, or tied to a particular issuer; the user report above is real and recent, so both appear to exist. If you are writing a detection, match on both. See [7002121](https://brambrinkmeier.com/entra/aadsts/7002121/) for the lab-reproduced page.",
  "sections": [
    {
      "heading": "What does AADSTS700212 mean?",
      "text": "Microsoft's [error code reference](https://learn.microsoft.com/entra/identity-platform/reference-error-codes) doesn't list AADSTS700212 (checked 30 October 2026). We found no other Microsoft page that names it either, so the wording below comes from user reports. A user on Microsoft Q&A, running a workload on Azure Kubernetes Service, posted this response (cluster values shortened):\n\n> AADSTS700212: No matching federated identity record found for presented assertion audience '`https://uaenorth.oic.prod-aks.azure.com/<tenant>/<guid>/,https://<cluster>.hcp.uaenorth.azmk8s.io,...`'. Please note that the matching is done using a case-sensitive comparison. Please check your federated identity credential Subject, Audience and Issuer against the presented assertion.\n\nThe response had `\"error\":\"invalid_client\"`. The audience the user's token carried was the cluster's own URLs, not `api://AzureADTokenExchange`.\n\nWhat Microsoft does document is the audience rule. A federated identity credential lists the audience Entra accepts. Microsoft's Graph overview: it *\"says what Microsoft identity platform should accept in the `aud` claim in the incoming token\"* and *\"should be set to `api://AzureADTokenExchange` for Microsoft Entra ID\"*. The restrictions page: *\"You must add a single audience value\"*. Matching is case-sensitive.\n\n700212 looks like the audience-specific form of [70021](https://brambrinkmeier.com/entra/aadsts/70021/); Microsoft doesn't say so. Its siblings are [700211](https://brambrinkmeier.com/entra/aadsts/700211/) (issuer) and [700213](https://brambrinkmeier.com/entra/aadsts/700213/) (subject); [70025](https://brambrinkmeier.com/entra/aadsts/70025/) means the app has no federated credentials at all.\n\n**A lab test on 30 October 2026 returned a different code for this case.** With a federated credential whose audience didn't match the presented token, my lab tenant answered with [`AADSTS7002121`](https://brambrinkmeier.com/entra/aadsts/7002121/), not 700212:\n\n> AADSTS7002121: No matching federated identity record found for presented assertion audience '<audience>'. The Audience for the federated identity credential...\n\nIn the same run, a wrong issuer gave [700211](https://brambrinkmeier.com/entra/aadsts/700211/) and a wrong subject gave [700213](https://brambrinkmeier.com/entra/aadsts/700213/), so those two are confirmed. For the audience, 7002121 is what Entra returns today on a current tenant. We can't tell from one tenant whether 700212 is an older code, a different-cloud code, or tied to a particular issuer; the user report above is real and recent, so both appear to exist. If you are writing a detection, match on both. See [7002121](https://brambrinkmeier.com/entra/aadsts/7002121/) for the lab-reproduced page."
    },
    {
      "heading": "Is it an attack?",
      "text": "No. It means the workload asked its own provider for a token meant for something else.\n\n| Pattern | Likely meaning |\n|---|---|\n| Kubernetes pod fails and the audience in the error is the cluster URL | The pod is using its default service account token, not a token projected for `api://AzureADTokenExchange`. Users report this; the Q&A thread above is one case. |\n| GitHub Actions fails after changing the `audience` input | The workflow asks GitHub for a token with an audience that the credential doesn't list. The Azure/login README: *\"The default `audience` is `api://AzureADTokenExchange`.\"* |\n| A non-public Azure cloud | The Azure/login README notes that for non-public clouds *\"the `audience` will also need to be updated.\"* Presumably the credential and the workflow must agree on the cloud's value. |\n| Case differs only (`api://azureadtokenexchange`) | Microsoft has moved to case-sensitive matching in stages since 2024. Fix the case on whichever side is wrong. |"
    },
    {
      "heading": "How do I find it in my logs (KQL)?",
      "text": "Microsoft doesn't document which sign-in table 700212 lands in. Our reading: app registration failures land in `AADServicePrincipalSignInLogs`; a user-assigned managed identity may log in the managed identity sign-in log instead. Check:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Rows = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceTable\n```\n\nFailures per app with the error text. Whether `ResultDescription` (documented as *\"the error description for the sign-in operation\"*) carries the presented audience is our assumption; check a row:\n\n```kusto\nAADServicePrincipalSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Failures = count(),\n            FirstSeen = min(TimeGenerated),\n            LastSeen = max(TimeGenerated),\n            Descriptions = make_set(ResultDescription, 5),\n            IPs = make_set(IPAddress, 10)\n          by AppId, ServicePrincipalName\n| sort by Failures desc\n```"
    },
    {
      "heading": "How do I fix AADSTS700212?",
      "text": "**For developers and pipeline owners:**\n1. Read the audience from the error message, or decode the token your workload sends and read `aud`.\n2. Make the workload request its token for `api://AzureADTokenExchange`, unless you deliberately chose another value. Microsoft: *\"For security reasons, you should pick a value that is unique for tokens meant for Microsoft Entra ID.\"*\n3. Kubernetes: the token Entra receives must be a projected service account token with that audience. The answer in the Q&A thread shows a projected volume with `audience: api://AzureADTokenExchange`, mounted at `/var/run/secrets/azure/tokens`. The same answer says the workload identity webhook should inject this when workload identity is enabled. If the mount is missing, check that first.\n4. GitHub Actions: leave the `audience` input of `azure/login` at its default unless the credential says otherwise.\n\n**For admins:**\n1. Check the credential's audience. It holds exactly one value, and Microsoft recommends `api://AzureADTokenExchange`.\n2. If you changed it from the default, make sure every workload that uses the credential requests that same value, in the same case."
    }
  ],
  "microsoftWording": {
    "text": "AADSTS700212: No matching federated identity record found for presented assertion audience '`https://uaenorth.oic.prod-aks.azure.com/<tenant>/<guid>/,https://<cluster>.hcp.uaenorth.azmk8s.io,...`'. Please note that the matching is done using a case-sensitive comparison. Please check your federated identity credential Subject, Audience and Issuer against the presented assertion.",
    "source": "Quoted on the page from the lab or from user reports; the code is not in Microsoft's error code reference",
    "sourceUrl": null
  },
  "securityReading": {
    "category": {
      "id": "app-config",
      "label": "App configuration"
    },
    "kind": {
      "id": "config",
      "label": "Configuration error"
    },
    "attackSignal": {
      "level": "low",
      "label": "Low: usually configuration or user error"
    },
    "mitre": [],
    "tables": [],
    "isItAnAttack": "No. It means the workload asked its own provider for a token meant for something else.\n\n| Pattern | Likely meaning |\n|---|---|\n| Kubernetes pod fails and the audience in the error is the cluster URL | The pod is using its default service account token, not a token projected for `api://AzureADTokenExchange`. Users report this; the Q&A thread above is one case. |\n| GitHub Actions fails after changing the `audience` input | The workflow asks GitHub for a token with an audience that the credential doesn't list. The Azure/login README: *\"The default `audience` is `api://AzureADTokenExchange`.\"* |\n| A non-public Azure cloud | The Azure/login README notes that for non-public clouds *\"the `audience` will also need to be updated.\"* Presumably the credential and the workflow must agree on the cloud's value. |\n| Case differs only (`api://azureadtokenexchange`) | Microsoft has moved to case-sensitive matching in stages since 2024. Fix the case on whichever side is wrong. |",
    "conditionalAccess": null
  },
  "kql": [
    {
      "title": null,
      "leadIn": "Microsoft doesn't document which sign-in table 700212 lands in. Our reading: app registration failures land in `AADServicePrincipalSignInLogs`; a user-assigned managed identity may log in the managed identity sign-in log instead. Check:",
      "query": "union withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Rows = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceTable"
    },
    {
      "title": null,
      "leadIn": "Failures per app with the error text. Whether `ResultDescription` (documented as *\"the error description for the sign-in operation\"*) carries the presented audience is our assumption; check a row:",
      "query": "AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Failures = count(),\n            FirstSeen = min(TimeGenerated),\n            LastSeen = max(TimeGenerated),\n            Descriptions = make_set(ResultDescription, 5),\n            IPs = make_set(IPAddress, 10)\n          by AppId, ServicePrincipalName\n| sort by Failures desc"
    }
  ],
  "kqlSectionText": "Microsoft doesn't document which sign-in table 700212 lands in. Our reading: app registration failures land in `AADServicePrincipalSignInLogs`; a user-assigned managed identity may log in the managed identity sign-in log instead. Check:\n\n```kusto\nunion withsource=SourceTable SigninLogs, AADNonInteractiveUserSignInLogs, AADServicePrincipalSignInLogs\n| where TimeGenerated > ago(30d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Rows = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceTable\n```\n\nFailures per app with the error text. Whether `ResultDescription` (documented as *\"the error description for the sign-in operation\"*) carries the presented audience is our assumption; check a row:\n\n```kusto\nAADServicePrincipalSignInLogs\n| where TimeGenerated > ago(7d)\n| where ResultType == \"700212\" or ResultSignature == \"700212\"\n| summarize Failures = count(),\n            FirstSeen = min(TimeGenerated),\n            LastSeen = max(TimeGenerated),\n            Descriptions = make_set(ResultDescription, 5),\n            IPs = make_set(IPAddress, 10)\n          by AppId, ServicePrincipalName\n| sort by Failures desc\n```",
  "fix": {
    "short": "Pipeline owner: read the audience from the error or the token's `aud` claim, and request the token for `api://AzureADTokenExchange` (or the credential's value, same case). Kubernetes: use a projected service account token with that audience. GitHub Actions: leave `audience` at its default.",
    "steps": "**For developers and pipeline owners:**\n1. Read the audience from the error message, or decode the token your workload sends and read `aud`.\n2. Make the workload request its token for `api://AzureADTokenExchange`, unless you deliberately chose another value. Microsoft: *\"For security reasons, you should pick a value that is unique for tokens meant for Microsoft Entra ID.\"*\n3. Kubernetes: the token Entra receives must be a projected service account token with that audience. The answer in the Q&A thread shows a projected volume with `audience: api://AzureADTokenExchange`, mounted at `/var/run/secrets/azure/tokens`. The same answer says the workload identity webhook should inject this when workload identity is enabled. If the mount is missing, check that first.\n4. GitHub Actions: leave the `audience` input of `azure/login` at its default unless the credential says otherwise.\n\n**For admins:**\n1. Check the credential's audience. It holds exactly one value, and Microsoft recommends `api://AzureADTokenExchange`.\n2. If you changed it from the default, make sure every workload that uses the credential requests that same value, in the same case."
  },
  "labReproduced": null,
  "labSectionText": null,
  "labRuns": [
    {
      "date": "2026-10-30",
      "run": "round 5",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/69",
      "scenario": "Federated credential: audience does not match",
      "expected": "700212 / 700213 / 70021",
      "got": "7002121",
      "matchedExpectation": false,
      "message": "AADSTS7002121: No matching federated identity record found for presented assertion audience '{audience-id}'. The Audience for the federat"
    },
    {
      "date": "2026-10-30",
      "run": "round 6",
      "method": "token-endpoint requests against a lab tenant, using lab-created test apps",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/70",
      "scenario": "Federated credential: audience does not match",
      "expected": "700212 / 700213 / 70021",
      "got": "7002121",
      "matchedExpectation": false,
      "message": "AADSTS7002121: No matching federated identity record found for presented assertion audience '{audience-id}'. The Audience for the federat"
    },
    {
      "date": "2026-10-30",
      "run": "round 7",
      "method": "token-endpoint requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/71",
      "scenario": "Federated credential: audience does not match",
      "expected": "700212 / 700213 / 70021",
      "got": "7002121",
      "matchedExpectation": false,
      "message": "AADSTS7002121: No matching federated identity record found for presented assertion audience '{audience-id}'. The Audience for the federat"
    },
    {
      "date": "2026-10-31",
      "run": "round 8",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/72",
      "scenario": "Federated credential: audience does not match",
      "expected": "700212 / 700213 / 70021",
      "got": "7002121",
      "matchedExpectation": false,
      "message": "AADSTS7002121: No matching federated identity record found for presented assertion audience '{audience-id}'. The Audience for the federat"
    },
    {
      "date": "2026-10-31",
      "run": "round 9",
      "method": "token-endpoint and device-code requests against a lab tenant",
      "issue": "https://github.com/GrantZero604/Brambrinkmeier.com/issues/73",
      "scenario": "Federated credential: audience does not match",
      "expected": "700212 / 700213 / 70021",
      "got": "7002121",
      "matchedExpectation": false,
      "message": "AADSTS7002121: No matching federated identity record found for presented assertion audience '{audience-id}'. The Audience for the federat"
    }
  ],
  "verification": "documentation",
  "related": [
    "7002121",
    "70021",
    "700211",
    "700213",
    "7002138",
    "70025",
    "700226"
  ],
  "coveredBy": [],
  "sources": [
    {
      "title": "Microsoft Entra authentication and authorization error codes",
      "url": "https://learn.microsoft.com/lb-lu/entra/identity-platform/reference-error-codes"
    },
    {
      "title": "Migration to Workflow credential identity is throwing error (Microsoft Q&A)",
      "url": "https://learn.microsoft.com/en-ca/answers/questions/2006914/migration-to-workflow-credential-identity-is-throw"
    },
    {
      "title": "Important considerations and restrictions for federated identity credentials",
      "url": "https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-considerations"
    },
    {
      "title": "Overview of federated identity credentials (Microsoft Graph)",
      "url": "https://learn.microsoft.com/graph/api/resources/federatedidentitycredentials-overview"
    },
    {
      "title": "Configure an app to trust an external identity provider",
      "url": "https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust"
    },
    {
      "title": "Federated identity credentials and case-sensitive matching (Microsoft Entra breaking changes, August 2024)",
      "url": "https://learn.microsoft.com/en-us/entra/identity-platform/reference-breaking-changes"
    },
    {
      "title": "Azure/login GitHub Action README",
      "url": "https://github.com/Azure/login#readme"
    },
    {
      "title": "AADServicePrincipalSignInLogs table reference (Azure Monitor)",
      "url": "https://learn.microsoft.com/en-us/azure/azure-monitor/reference/tables/aadserviceprincipalsigninlogs"
    }
  ],
  "published": "2026-10-30",
  "lastUpdated": "2026-10-30",
  "lastVerified": null,
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "licenseNote": "CC BY 4.0 covers the author's own text and classification fields. Quoted Microsoft error wording belongs to Microsoft, is quoted with its source and can change.",
  "attribution": "Bram Brinkmeier, \"AADSTS700212: Federated credential audience doesn't match (fix)\", https://brambrinkmeier.com/entra/aadsts/700212/ (CC BY 4.0)",
  "microsoftErrorLookup": "https://login.microsoftonline.com/error?code=700212"
}
