GitHub Actions OIDC to Azure: "AADSTS700213: No matching federated identity record found"
GitHub gave the job a token, and Microsoft Entra ID looked for a federated identity credential on the app registration or managed identity whose subject is exactly the token's sub. None matched. Unlike AWS, Entra prints the subject it received, so the fix is to make a credential that says the same thing, character for character.
What it looks like
Federated token details: issuer - https://token.actions.githubusercontent.com subject claim - repo:octo-org@123456/octo-repo@456789:environment:dev audience - api://AzureADTokenExchange job_workflow_ref - octo-org/octo-repo/.github/workflows/deploy.yml@refs/heads/main ... AADSTS700213: No matching federated identity record found for presented assertion subject 'repo:octo-org@123456/octo-repo@456789:environment:dev'. Check your federated identity credential Subject, Audience and Issuer against the presented assertion. Error: Login failed with Error: The process '/usr/bin/az' failed with exit code 1. Double check if the 'auth-type' is correct. Refer to https://github.com/Azure/login#readme for more information.
The last line is azure/login passing on the failure of az login. The auth-type it mentions is not the problem here; the subject is. azure/login prints the token's details before it logs in, so the subject is in the log twice.
Which side has the IDs
Compare the subject in the error with the subjects of the credentials:
az ad app federated-credential list --id APP-OBJECT-ID --query "[].{name:name,subject:subject}"
For a user-assigned managed identity, az identity federated-credential list --identity-name NAME --resource-group GROUP. Then:
- The error has
@and numbers, and the credential does not: the repository uses GitHub's immutable subject. From GitHub's changelog of April 23, 2026: "All repositories created after July 15, 2026 will automatically use the new immutable subject claim format," and "Repository renames and transfers after July 15, 2026 will also adopt the new format." Older repositories get it when someone opts in. October reports: an environment deploy whose subject ended in:environment:dev(Dorfplatz #78), and a setup script that wrote the name-based subject, exited 0, and left the deploy failing (TALXIS lab #98). Add a credential with the subject from the error. - The credential has the IDs, and the error does not: the other way round. In the Azure portal's form for GitHub Actions, the owner and repository ID fields are optional, and filling them in writes the immutable form (the azure/login troubleshooting section). A repository created before July 15, 2026 still sends names only, so nothing matches (azure/login #617). Either remove the IDs from the credential, or turn on immutable subjects for the repository in its OIDC settings, which is where Microsoft's migration guide goes.
- Both have the same form, and the text still differs: it is the part after the repository (next section) or the case. Entra compares subjects case-sensitively, and a difference in case gets its own code,
AADSTS7002138, with "The subject matches with case-insensitive comparison, but not with case-sensitive comparison."
To see the prefix your repository uses without running a job:
gh api repos/OWNER/REPO/actions/oidc/customization/sub
The answer has use_immutable_subject and sub_claim_prefix, such as repo:octo-org@123456/octo-repo@456789.
Add the credential
From Microsoft's guide Migrate GitHub Actions federated credentials to immutable subjects, for an app registration, with the body in credential.json:
{
"name": "octo-repo-dev",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:octo-org@123456/octo-repo@456789:environment:dev",
"audiences": ["api://AzureADTokenExchange"]
}
az ad app federated-credential create --id APP-OBJECT-ID --parameters ./credential.json
For a user-assigned managed identity:
az identity federated-credential create --name octo-repo-dev \ --identity-name NAME --resource-group GROUP \ --issuer https://token.actions.githubusercontent.com \ --subject 'repo:octo-org@123456/octo-repo@456789:environment:dev' \ --audiences api://AzureADTokenExchange
- One credential matches one subject. A workflow that deploys from
mainand through two environments needs three, or one flexible federated identity credential: the guide's example matchesrepo:octo-org@123456/octo-repo@456789:*in aclaimsMatchingExpressionand also checksrepository_id. - Keep the old credential until the new one works, then delete it. That is the order in Microsoft's guide.
- Right after a credential is added or changed, a login can fail for a few minutes with
AADSTS70021: No matching federated identity record found for presented assertion.while the change spreads. Microsoft's considerations page says to wait a short time and retry. - A script, Bicep or Terraform file that builds the subject from
repo:${owner}/${repo}writes the old form for every new repository. Readingsub_claim_prefixfrom the API above is what the fix for TALXIS lab #98 suggests.
The part after the repository
- A job with
environment:gets…:environment:NAMEin place of the ref. - A
pull_requestrun gets…:pull_request, but only when the job has no environment. - Otherwise it is the ref:
…:ref:refs/heads/main, or…:ref:refs/tags/v1.2.0for a tag. - A colon inside a value becomes
%3A, so an environment namedProduction:V1appears asenvironment:Production%3AV1. - A custom template on the repository or organization (
include_claim_keys) changes the wholesub. With immutable claims, GitHub's documentation says the owner and repository IDs stay in it even then.
The job never asked for a token
Without id-token: write there is no token and no AADSTS code. azure/login logs "Failed to fetch federated token from GitHub. Please make sure to give write permissions to id-token in the workflow." and then fails with "Login failed with Error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable."
permissions: id-token: write contents: read
A permissions: block on the job replaces the workflow's for that job, so it needs id-token: write too. Pull requests from forks and reusable workflows have limits of their own: "Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable".
Telling it apart
AADSTS7002138: the same subject with different capital letters.AADSTS70021right after you changed a credential: the change has not spread yet.AADSTS700016: Entra did not find the application in the tenant. Theclient-idortenant-idis wrong, not the subject.- To Google Cloud, a condition on the name-based
subfails with "The given credential is rejected by the attribute condition". - To AWS, the same mismatch has no subject in the message: "Not authorized to perform sts:AssumeRoleWithWebIdentity".
See failed jobs from IntelliJ IDEA
Pipeline Inbox for GitHub Actions Workflows, a plugin by Lesstep for JetBrains IDEs 2024.1 or later, lists the runs of all your repositories in an IDE tool window, with their jobs and steps, and your current branch's runs are in the status bar. Reading is free; re-running failed jobs, cancelling, running workflows and reviewing deployments are Pro (US$1.50 a month, with a 30-day trial).
GitHub and GitHub Actions are trademarks of GitHub, Inc.; Microsoft Azure and Microsoft Entra are trademarks of Microsoft Corporation; IntelliJ IDEA is a trademark of JetBrains s.r.o. This page is not affiliated with, sponsored or endorsed by GitHub, Inc., Microsoft or JetBrains.
More: GitHub Actions OIDC to AWS · Approving a run that is "Waiting" for review · npm publish "ENEEDAUTH" · GitHub Actions plugins for IntelliJ IDEA compared · 日本語