Branch-Scoped Federated Credential Subjects Grant Azure Token Exchange to Every Workflow on That Branch
A federated identity credential scoped to a GitHub branch grants Azure AD token exchange to every workflow on that branch, not just the intended deployment job.
Operational summary
At a glance
- Symptom
- The federated identity credential authenticates cleanly for the intended GitHub Actions deployment job, yet Microsoft Entra ID sign-in logs later show the same application…
- Likely cause
- The root cause is a mismatch between how Microsoft Entra ID validates a federated credential's subject and how GitHub Actions constructs that subject for…
- Impact
- The practical impact is that a one-job trust boundary the platform team believed they had created is, in reality, a repository-wide trust boundary tied only to branch protection.
- Verification signal
- Validation confirms that only the intended deployment workflow can still exchange tokens after the subject is narrowed, and that every other workflow on the same branch is rejected.List…
- Safe correction
- The correction replaces the branch-scoped subject with a GitHub Environment-scoped subject, so trust is tied to an environment gated by required reviewers rather than to every workflow on…
- Rollback or recovery
- Rollback restores the original branch-scoped federated credential from the backup captured before the change, and it must be available immediately if the narrowed credential blocks the legitimate deployment.az…
This trap affects Microsoft Entra ID applications configured for passwordless authentication from GitHub Actions using Azure Workload Identity Federation, where the federated identity credential is scoped to a branch rather than a protected environment.
Symptom
The federated identity credential authenticates cleanly for the intended GitHub Actions deployment job, yet Microsoft Entra ID sign-in logs later show the same application identity being used by workflow runs the platform team never approved. Operators usually notice this only during a routine sign-in log review or an unrelated incident investigation, when token exchanges appear with timestamps, actors or commit references that do not match the deployment pipeline’s normal pattern. The federated credential itself reports no error, no expiry warning and no configuration drift; from the app registration’s perspective every one of those token exchanges is a fully valid, correctly matched sign-in.
False Assumption
The team configuring the credential assumed that restricting its subject to repo:<org>/<repo>:ref:refs/heads/main narrowed trust to "the production deployment job that runs on main". In practice this subject string identifies the branch a workflow run was triggered from, not the specific workflow file, job or step that produced the token. Any workflow definition in the repository configured to run on pushes to main receives a token bearing the identical subject claim, and Entra ID’s federated credential match has no visibility into which workflow file issued it.
Root Cause
The root cause is a mismatch between how Microsoft Entra ID validates a federated credential’s subject and how GitHub Actions constructs that subject for branch-triggered runs. Entra ID performs an exact string match against the subject and issuer claims in the presented OIDC token; it does not inspect workflow file names, job identifiers or approval status. GitHub’s token issuer includes the branch reference in the subject specifically so trust can be scoped to a branch, but it does not encode which of potentially many workflow files running on that branch generated the token. A branch-scoped subject therefore authorises every current and future workflow file that triggers on that branch, not only the single deployment workflow the credential was created for.
Impact
The practical impact is that a one-job trust boundary the platform team believed they had created is, in reality, a repository-wide trust boundary tied only to branch protection. Anyone able to add or modify a workflow file that runs on the trusted branch, whether through an approved pull request merge, a dependency-update bot, or a compromised third-party Action, can obtain the same Azure AD token as the intended deployment pipeline and exercise whatever role assignments that service principal holds. Because Entra ID logs a successful, correctly matched sign-in in every case, there is no security alert distinguishing an authorised deployment from an unauthorised one; only correlation between Entra ID sign-in logs and GitHub’s own workflow run history reveals the difference, and most teams do not build that correlation by default.
Diagnosis
Confirming this trap requires comparing the federated credential’s configured subject against every workflow file capable of triggering on the trusted branch, then correlating Entra ID sign-in activity with GitHub’s workflow run history for the same period.
- List every federated credential configured on the affected application registration and record each subject, issuer and audience value.
- For each branch-scoped subject, enumerate the workflow files in the repository that trigger on that branch, including workflows added by automation such as dependency updates.
- Query Entra ID sign-in logs for the application’s service principal over a representative period and note the timestamps of every successful token exchange.
- Cross-reference those timestamps against GitHub Actions workflow run history to identify any sign-in that does not correspond to the intended deployment workflow.
Correction
The correction replaces the branch-scoped subject with a GitHub Environment-scoped subject, so trust is tied to an environment gated by required reviewers rather than to every workflow on a branch. This narrows the federated credential’s match to workflow runs that explicitly target the protected environment, and GitHub blocks the token request for jobs that omit that environment declaration, regardless of which branch they run on.
az ad app federated-credential show
--id $APP_OBJECT_ID
--federated-credential-id $CRED_ID
> original-credential-backup.json
az ad app federated-credential update
--id $APP_OBJECT_ID
--federated-credential-id $CRED_ID
--parameters narrowed-credential.json
The narrowed-credential.json subject value should be set to repo:<org>/<repo>:environment:<environment_name>, and the repository’s environment protection rules must require at least one reviewer before that environment name carries any additional trust value over the branch it replaces.
Validation
Validation confirms that only the intended deployment workflow can still exchange tokens after the subject is narrowed, and that every other workflow on the same branch is rejected.
- List the federated credential again and confirm the subject now reads
environment:<name>rather thanref:refs/heads/<branch>. - Trigger a test workflow run on the trusted branch that deliberately omits the protected environment declaration, and confirm the token exchange step fails.
- Re-run the legitimate deployment workflow and confirm it still obtains a token successfully, referencing the correct environment name.
- Review Entra ID sign-in logs for the following 24 to 48 hours and confirm every recorded sign-in for the application correlates with the approved deployment workflow run.
Rollback
Rollback restores the original branch-scoped federated credential from the backup captured before the change, and it must be available immediately if the narrowed credential blocks the legitimate deployment.
az ad app federated-credential update
--id $APP_OBJECT_ID
--federated-credential-id $CRED_ID
--parameters original-credential-backup.json
Treat a restored branch-scoped credential as a temporary measure rather than a resolution: confirm the deployment workflow succeeds, then re-diagnose the environment name mismatch or protection rule gap that caused the rollback before attempting the narrowed subject again.
Prevention
Preventing recurrence means treating every federated credential subject as equivalent in sensitivity to a long-lived bearer secret with the same scope, and reviewing it accordingly. Configure new federated credentials with environment-scoped subjects by default rather than branch-scoped ones, and require GitHub Environment protection rules with at least one reviewer before any environment is referenced in a federated credential. Include federated credential subjects in periodic access reviews alongside role assignments and secrets, and configure alerting on Entra ID sign-in activity for high-privilege service principals so an unexpected sign-in pattern is visible before it needs to be reconstructed from historical logs.
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
repo:<org>/<repo>:ref:refs/heads/mainmainaz ad app federated-credential show
--id $APP_OBJECT_ID
--federated-credential-id $CRED_ID
> original-credential-backup.json
az ad app federated-credential update
--id $APP_OBJECT_ID
--federated-credential-id $CRED_ID
--parameters narrowed-credential.jsonVerify, roll back or escalate
Verify
Validation confirms that only the intended deployment workflow can still exchange tokens after the subject is narrowed, and that every other workflow on the same branch is rejected.List the federated credential again and confirm the subject now reads environment:<name> rather…
Rollback
Rollback restores the original branch-scoped federated credential from the backup captured before the change, and it must be available immediately if the narrowed credential blocks the legitimate deployment.az ad app federated-credential update --id $APP_OBJECT_ID --federated-credential-id $CRED_ID --parameters…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.