GitHub Actions needs OIDC audience constraints

Lobsters Hottest Tools

Summary

This blog post argues that GitHub Actions lacks static OIDC audience constraints, unlike GitLab CI/CD, and that this design weakness poses an increasing security risk as OIDC-based federation becomes more common.

<p><a href="https://lobste.rs/s/ipt1em/github_actions_needs_oidc_audience">Comments</a></p>
Original Article
View Cached Full Text

Cached at: 08/10/26, 02:59 PM

# GitHub Actions needs OIDC audience constraints Source: [https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints) ## ENOSUCHBLOG ## *Programming, philosophy, pedaling\.* - [Home](https://blog.yossarian.net/) - [Tags](https://blog.yossarian.net/tags) - [Series](https://blog.yossarian.net/series) - [Favorites](https://blog.yossarian.net/favorites) - [Archive](https://blog.yossarian.net/archive) - [Main Site](https://yossarian.net/) - [TILs](https://yossarian.net/til) --- ## *Aug 10, 2026*Tags:[dear\-github](https://blog.yossarian.net/tags#dear-github),[oss](https://blog.yossarian.net/tags#oss),[security](https://blog.yossarian.net/tags#security) --- **TL;DR**: GitHub Actions should allow end\-users to express*audience constraints*, to make it harder for an attacker to pivot across services that use independent OIDC\-bearing jobs\. They could do this with relatively small syntax tweak, although the backend implications are probably nontrivial\. Like many CI/CD providers, GitHub Actions provides verifiable machine identities[1](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:ident)via[OpenID Connect](https://openid.net/developers/how-connect-works/)\(OIDC\)\. These are awesome for a lot of reasons, not least of which is that they allow workflows running on GitHub Actions to federate with other \(third\-party\) services*without*GitHub having to intermediate and pre\-bless every interaction\. This is the backbone of how both[Trusted Publishing](https://docs.pypi.org/trusted-publishers/)and[Sigstore](https://sigstore.dev/)work: an individual workflows on GitHub Actions presents its machine identity \(via an OIDC token\) to an external service, which then authenticates and for some purpose \(uploading to PyPI or signing artifacts, respectively\)\. Unfortunately, GitHub’s*mechanism*for exposing OIDC tokens in workflows contains a significant weakness, one that \(in my opinion\) will present an increasingly serious security risk over time\. This post is about that weakness\. ## CI/CD and OIDC[\#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#cicd-and-oidc) At the core of all “OIDC in CI/CD” implementations is*some*mechanism that allows the workflow \(pipeline definition, etc\.\) to request or otherwise be pre\-loaded with an OIDC identity\. Here’s how that looks in GitLab CI/CD: ``` 1 2 3 4 5 6 my-job: id_tokens: PYPI_ID_TOKEN: aud: pypi script: | do-something.sh --id-token "${PYPI_ID_TOKEN}" ``` and here’s the equivalent in GitHub Actions: ``` 1 2 3 4 5 6 7 8 9 10 11 my-job: runs-on: ubuntu-latest permissions: id-token: write steps: run: | resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \ "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi") oidc_token=$(jq '.value' <<< "${resp}") do-something.sh --token "${oidc_token}" ``` The difference between these two is small but important: GitLab requires the*OIDC audience*\(the`aud`\) to be**declared up\-front and statically**, while GitHub requires the workflow to**dynamically request**a token with an audience selected at*runtime*\(the`audience`parameter in the HTTP request\)\. ## Why does this matter?[\#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#why-does-this-matter) First, a very quick foray into OIDC\. Under the hood, OIDC is \(mostly\) just[OAuth 2\.0](https://oauth.net/2/), and OIDC identity tokens are just[JSON Web Tokens](https://datatracker.ietf.org/doc/html/rfc7519)\(JWTs\), with some additional[2](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:stringent)constrains on the claims they should express\. The most important claim in an OIDC ID token is arguably`sub`, since it identifies the principal \(the “subject”\)[3](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:sub)\. However, the*second*most important claim is`aud`, for the*audience*\. The audience is*critical*because it**constrains who honors the token**: services that accept ID tokens should only do so when they recognize the audience as matching theirs\. In other words: the`aud`claim prevents an ID token that’s*intentionally*been issued for a specific service from being stolen by the attacker and*mis\-applied*to another service\. This is intended as a defense\-in\-depth: even if an attacker manages to exfiltrate an OIDC credential, they should not be able to*pivot*to other services it\. ![](https://blog.yossarian.net/assets/oidc-aud.jpg) It’s a flimsy defense but one that’s*generally*effective[4](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:effective),**unless**you give the attacker the ability to control the`aud`claim as well\. Unfortunately, that’s exactly what GitHub Actions enables[5](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#fn:others):`id\-token: write`gives the job \(or entire workflow\) the ability to mint*any*ID token it pleases, with*any*audience\. This matters a great deal in a world \(our world\) where jobs that are given`id\-token: write`*also*run a lot of third\-party code: any vulnerability \(or malware\) in that code has the potential to ask for*new*ID tokens for audiences that it isn’t*supposed*to have access to\. We thought about this problem when designing Trusted Publishing, and came to the conclusion that the machine identity that Trusted Publishing uses*must*include the workflow name, preventing an attacker from impersonating`pypi\-publish\.yml`by inducing an ID token from`aws\-deploy\.yml`with`aud: pypi`\. However, this constraint is not suitable for all possible use cases: many integrations want to use*just*the`org/repo`slug as a sufficient identity, meaning that all workflows are effectively co\-equal when issuing ID tokens\. ## What should GitHub do?[\#](https://blog.yossarian.net/2026/08/10/github-actions-needs-oidc-audience-constraints#what-should-github-do) Add constraints\! Ideally, something like this: ``` 1 2 3 4 my-job: runs-on: ubuntu-latest permissions: id-token: [pypi] ``` The basic idea here is to*constrain*what audiences the job can request ID tokens for\. In the example above, attempting to request a token for`aud: sts\.amazonaws\.com`would cause an error, preventing a job that’s*intended*only for publishing to PyPI from serving as a pivot to AWS\. There are, of course, some potential downsides to this\. For example, some services might \(inadvisedly\) require dynamic information in their audience, meaning that a value can’t be statically pre\-declared\. I think this is rare enough in practice to not be worth blocking a security improvement and, when they*do*occur, GitHub could continue to allow the`id\-token: write`form as a \(less secure\!\) catch\-all\. --- ---

Similar Articles

GitHub is the wrong shape for this new world

Hacker News Top

This blog post argues that GitHub's collaboration paradigm (branches, pull requests, code reviews) is ill-suited for the modern AI-driven software development era where LLMs and agents generate code at high velocity, calling for rethinking of tools and workflows.

GitHub's take on age assurance for developers

Hacker News Top

GitHub's blog post explains how age assurance laws aimed at protecting minors online could inadvertently burden open source developers and infrastructure, and urges policymakers to scope such laws appropriately.