Renamed GitHub Repos Inherit Azure's OIDC Trust
A Federated Identity Credential on an Azure Managed Identity keeps trusting a GitHub repository subject string long after that repository name is freed and reclaimed by an unrelated account. Azure AD's token exchange endpoint checks only the issuer, subject and audience tuple — not ownership continuity — so the new owner's workflow mints a valid OIDC token and inherits the identity's RBAC grants undetected.
Operational summary
At a glance
- Symptom
- The original GitHub organisation or repository is renamed or deleted and the freed name is re-registered by a different account.
- Likely cause
- A Federated Identity Credential's subject claim is bound to a mutable GitHub org/repo name rather than an immutable repository ID.
- Impact
- The new repository owner's Actions workflow mints an OIDC token matching the stale subject and exchanges it for the Managed Identity's Azure access token.
- Verification signal
- Re-run the detection test and a controlled negative-path test.
- Safe correction
- Pin trust to job_workflow_ref with a fixed commit SHA and audit federated credentials against GitHub's numeric repository_id, not display name.
- Rollback or recovery
- Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
The Trap
Federated Identity Credential subject binding to a GitHub repository by name rather than by its immutable repository ID.
The Default State
When you create a Federated Identity Credential on a User-Assigned Managed Identity or App Registration for GitHub Actions OIDC, the portal wizard writes the Subject identifier as repo:<org>/<repo>:ref:refs/heads/<branch> or repo:<org>/<repo>:environment:<name>. The Issuer is fixed at https://token.actions.githubusercontent.com and the Audience defaults to api://AzureADTokenExchange. Azure AD stores this as a static three-field tuple — issuer, subject, audience — and performs an exact string match against the sub, iss and aud claims presented in the client_assertion JWT during the OAuth2 exchange at /oauth2/v2.0/token. Nothing in that tuple references GitHub’s internal repository_id, and nothing re-validates ownership after the credential is created.
The Blast Radius
GitHub permits an organisation or repository name to be freed and immediately re-registered by an unrelated account the moment the original is renamed or deleted — a documented repojacking pattern. If your Federated Identity Credential still contains the old literal subject string, any new owner of that org/repo name — a different GitHub tenant, a separate business unit, or an external actor — can push a workflow run that mints an OIDC token with an identical sub claim. Azure AD’s token exchange endpoint has no visibility into GitHub’s repository lifecycle; it only checks the tuple match, so it issues a valid access token scoped to whatever role assignments the Managed Identity carries. The new owner inherits that RBAC, Key Vault access, or storage permission set, and Azure AD sign-in logs show nothing more suspicious than a routine federated token exchange from a trusted issuer.
The Lead Mechanic Fix
Audit every credential with az ad app federated-credential list –id <app-id> and cross-reference each subject string’s repository name against GitHub’s numeric repository_id via the REST API, not the display name. Where the workflow supports it, bind trust to the job_workflow_ref claim pinned to a specific reusable workflow path and commit SHA instead of a mutable branch ref, and scope Audience to a per-environment value rather than the shared default. Run a scheduled Azure Resource Graph query against federatedIdentityCredentials to flag any subject referencing a repository that no longer resolves under its original owner, and make credential removal a mandatory step in the repository transfer and deletion runbook, not an optional cleanup task.
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.
Pin trust to job_workflow_ref with a fixed commit SHA and audit federated credentials against GitHub's numeric repository_id, not display name.
Validate the vendor-specific syntax in official documentation before applying it.
Verify, roll back or escalate
Verify
Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.
Rollback
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.