TL;DR: OIDC federation. The CI platform issues a signed, short-lived token describing the workflow, and the cloud provider is configured to trust that issuer and exchange the token for temporary credentials. There is no reusable long-lived cloud key in CI storage. Temporary tokens and role credentials still exist during the run and can leak. The trust condition is the part that must be right, because a loose one lets any repository assume your role.
How to approach it
State what the change removes rather than what it adds. Then spend the answer on the trust policy conditions, because a misconfigured OIDC setup is worse than the key it replaced and that is the interesting failure.
A strong answer
A stored access key is a bearer credential: anyone holding it is you, it does not expire, and it is copied into a secret store, a runner's environment, and probably a developer's laptop at some point. Every control around it (rotation, scanning, masking) is mitigation for the fact that it exists.
OIDC removes it. The CI platform runs an identity provider that signs a JWT describing the run: which repository, which branch or tag, which workflow, which environment. The cloud provider is configured to trust that issuer's public keys and to exchange a valid token for a short-lived role session, typically an hour. Trust configuration persists, and the runner receives temporary bearer credentials that must be kept out of logs and artifacts.
The example uses the legacy repository-name subject format. GitHub documents an immutable ID-based default for repositories created after July 15, 2026 (excluding GHES). Inspect the actual sub and any organization customization before writing the exact trust condition; do not widen a failing condition to an organization wildcard.
The mechanism is a standard trust relationship:
{
"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" },
"StringLike": { "token.actions.githubusercontent.com:sub": "repo:myorg/myrepo:ref:refs/heads/main" }
}
}
The sub condition is the whole security boundary and it is where implementations go wrong. Trusting the issuer alone means any GitHub repository in the world can assume your role, because they all get tokens from the same issuer. A condition of repo:myorg/* trusts every repository in your organisation, including a new one someone creates. The condition should name the repository and, for anything touching production, the specific branch or environment, so a pull request from a fork cannot obtain production credentials.
Fork-token permissions depend on the event and repository settings. In particular, a privileged pull_request_target workflow must not check out and execute untrusted pull-request code before using deployment credentials. Branch/environment claims do not make that code trusted. GitHub OIDC security.
Then the usual scoping still applies. The role should have least privilege for what the pipeline does, and separate roles per environment so the deploy-to-staging workflow cannot reach production. Federation removes persistent cloud-key storage; it does not reduce blast radius, and those are different problems.
The same pattern applies across providers: workload identity federation on GCP, federated credentials on Azure, and JWT auth in Vault. It is also how a Kubernetes pod should authenticate to a cloud provider, using the service account token rather than a mounted key.
What interviewers probe next
"What if the CI provider is compromised?" Then tokens can be minted for your repositories, and federation does not save you. That is an argument for tight conditions and least privilege, not against federation, since a stored key would also be readable in that scenario.
"How do you audit it?" CloudTrail records the assumed role session with the token's subject, so you can see which workflow and which ref obtained credentials. That is better provenance than a shared key gives you.
"What cannot use this?" Anything without an OIDC identity: a legacy runner, a cron job on a VM. Those need a different answer, usually an instance role or a Vault agent, and enumerating them is the migration work.
Common mistakes
Configuring the issuer without a sub condition, which is a world-readable role and strictly worse than the key it replaced.
Using a wildcard over the organisation, which quietly grants access to every future repository.
Treating it as a rotation improvement. It is a removal, and the distinction matters because long-lived CI cloud keys disappear, while temporary credentials, issuer keys and trust policies retain their own lifecycle.