
GitHub Actions Workflow Lockfiles Are Coming
If you manage cloud access for more than a handful of repositories, you've probably felt the friction. Every new repo needs its own cloud role, its own trust policy, its own subject claim match. Multiply that across AWS, Azure, and GCP, and you've got hundreds of trust policies to keep in sync with the state of your GitHub organization.
GitHub's March 12, 2026 release adds repository custom properties to OIDC token claims, and it changes the model fundamentally. Instead of writing trust policies that match specific repository names or paths, you write policies that match attributes: environment type, team ownership, compliance tier. Any repo with that attribute gets access automatically. New repos inherit the right policies the moment their properties are set. The allow list disappears.
When a GitHub Actions job runs, GitHub's OIDC provider can issue a signed JWT on its behalf. That token contains a set of claims describing the job: which repo it came from, which branch, which environment, which workflow. Your cloud provider validates the token's signature against GitHub's public keys, checks that the claims match the conditions in the trust policy, and if everything lines up, issues a short-lived access token.
No static secrets to rotate. No credentials stored in GitHub. The workflow proves its identity at runtime, and the credential expires when the job ends. The built-in claims cover a lot: repository, ref, environment, repository_owner, and more. But they're all about the job context: where the code lives and how the workflow was triggered. None of them carry org-level metadata about the repo's purpose or classification.
Organization and enterprise admins can now select custom properties to include in OIDC tokens. Once a property is added, every repository in the org with a value set for that property will automatically include it in its OIDC tokens, prefixed with repo_property_. If your org has a deployment_tier custom property set to production on a repo, the OIDC token for that repo's workflows will include "repo_property_deployment_tier": "production".
An example token now looks like this:
{
"sub": "repo:my-org/payments-service:environment:prod",
"repository": "my-org/payments-service",
"environment": "prod",
"repository_owner": "my-org",
"ref": "refs/heads/main",
"repo_property_deployment_tier": "production",
"repo_property_team": "platform",
"repo_property_compliance": "pci",
"iss": "https://token.actions.githubusercontent.com"
}Those repo_property_* claims are whatever your org chose to expose: team ownership, environment classification, compliance scope. They're org-managed, not workflow-managed. A developer doesn't write compliance: pci in a YAML file and hope someone notices. That classification is set by an admin at the org level and flows automatically.
The release ships a new settings page (currently in public preview) at the repository, organization, and enterprise level. Organization admins navigate to the Actions OIDC settings page and select which custom properties to include as claims. Once a property is added there, every repo with that property set will include it automatically — no workflow changes required.
You can also configure it via the REST API if you prefer infrastructure-as-code for your org settings:
# Add a custom property to OIDC token claims for an org
curl -L \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
https://api.github.com/orgs/{org}/actions/oidc/customization/sub \
-d '{"include_claim_keys": ["repo_property_deployment_tier", "repo_property_team"]}'Note that custom properties must already be defined at the org or enterprise level before they can be included in OIDC claims. You're not creating new properties here — you're choosing which existing ones appear in the token.
The traditional approach pins access to specific repositories by matching against the sub claim. An AWS IAM role trust policy for a production deployment role might look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": [
"repo:my-org/payments-service:environment:prod",
"repo:my-org/auth-service:environment:prod",
"repo:my-org/api-gateway:environment:prod"
]
}
}
}
]
}Every new production service requires a policy update. If you have 50 production repos, this list has 50 entries. If someone renames a repository or changes the environment name, the trust policy breaks silently. The allow list lives in the cloud provider console, disconnected from the GitHub org where the actual classification decisions are made.
With custom property claims, the same AWS trust policy becomes:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:repo_property_deployment_tier": "production"
}
}
}
]
}That's it. Any repository in your org with deployment_tier set to production can assume this role. New production repos get access automatically by having the right property. The trust policy never needs to change as your repository count grows.
The same custom property maps consistently across cloud providers, even though each provider uses its own OIDC trust syntax.
Azure federated identity credentials use a subject-based match by default, but you can reference additional OIDC claims in Azure AD Conditional Access policies or use the custom claims in your app registration's federated credential conditions. The subject field in a federated credential can be set to match a custom property using the full claim path. A practical pattern is to include the custom property in the customized sub claim template, so the federated credential subject pattern encodes both the repo context and the attribute:
{
"name": "production-repos",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo_property_deployment_tier:production",
"audiences": ["api://AzureADTokenExchange"]
}Alternatively, you can customize the OIDC subject claim template in your organization's settings to include the custom property value, and then match against that in the federated credential's subject field.
GCP Workload Identity Federation supports arbitrary attribute conditions using the Common Expression Language. The repo_property_* claims appear as attributes on the token, so you can reference them directly:
gcloud iam workload-identity-pools providers create-oidc github-provider \
--location="global" \
--workload-identity-pool="github-actions-pool" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--allowed-audiences="https://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-actions-pool/providers/github-provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.deployment_tier=assertion.repo_property_deployment_tier,attribute.team=assertion.repo_property_team" \
--attribute-condition="assertion.repo_property_deployment_tier == 'production'"The --attribute-mapping flag maps the OIDC claims to GCP attributes, and --attribute-condition enforces the access rule. You can then use IAM Conditions on Service Account bindings to further restrict which team attribute can impersonate which service account.
Here's the full flow from org setup to working deployment.
1. Define custom properties at the org level
In your GitHub organization settings, go to Custom properties and create the properties you want to use as access control dimensions. For example: deployment_tier with allowed values production, staging, development. Or team as a free-text string if you want to route by team name.
2. Tag your repositories
Set property values on each repository. This can be done through the repo settings UI, the org admin view (bulk edit), or the GitHub API. For large orgs, the API is the practical path:
curl -L \
-X PATCH \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer <YOUR_TOKEN>" \
https://api.github.com/repos/{owner}/{repo}/properties/values \
-d '{
"properties": [
{"property_name": "deployment_tier", "value": "production"},
{"property_name": "team", "value": "platform"}
]
}'3. Enable properties in OIDC claims
In your org's Actions OIDC settings (or via API), select the properties to include in tokens. Only properties selected here will appear as repo_property_* claims. Having this as an explicit allowlist is a useful gate: you control which metadata flows into your cloud trust policies.
4. Update your cloud trust policies
Replace per-repo sub claim lists with conditions on repo_property_* claims. Your workflows don't need to change — the token claims are injected by GitHub, not declared in the YAML.
The real win here isn't the shorter trust policy JSON — it's where the source of truth lives. With static subject claim matching, your cloud provider's IAM system becomes the implicit record of which repos have access to what. Someone creates a new service, puts it in production, and the question of whether it can deploy correctly gets answered by whether someone updated a role trust policy in three different cloud consoles. That's a coordination problem that scales with every new service.
With attribute-based policies, that coordination problem goes away. The classification decision — "this is a production service owned by the platform team" — is made once, in GitHub, by an org admin. It flows automatically to every cloud that uses repo_property_deployment_tier as a trust condition. The cloud IAM roles become stable policy artifacts rather than moving targets.
There's also a security benefit that's easy to miss: properties are org-managed. A workflow author can't write deployment_tier: production in their YAML to elevate access. The claim value comes from the organization's property configuration, not from the workflow file. Privilege escalation via a crafted workflow becomes harder.
The feature is currently in public preview, so the API and UI may change before GA. A few practical points:
For orgs already using OIDC keyless auth, migrating to property-based policies is mostly a cloud-side change: update the trust conditions to reference repo_property_* claims and tag your repos with the right properties. The GitHub-side configuration is a handful of API calls or a few clicks in the new settings UI. Start with a non-production role to validate the claim flow before touching production access.
Recommended for you
What's next in your stack.
GET TENKI