From 219f1e54b5508364f9c3cc945e15f528445c96f4 Mon Sep 17 00:00:00 2001 From: Domen Gabrovsek Date: Tue, 11 Aug 2026 14:14:58 +0200 Subject: [PATCH] fix(aws-credentials): default the OIDC audience to sts.amazonaws.com Every OIDC role assumption through this wrapper failed with "Could not assume role with OIDC: The web identity token provided could not be validated." The wrapped action declares `audience` with `default: sts.amazonaws.com`, but a GitHub Actions input default applies only when the input is absent, never when it is present and empty. Forwarding `${{ inputs.audience }}` with a local default of '' therefore passed an explicit empty string and clobbered it, so GitHub minted the token with aud=https://github.com/ and STS rejected it. Sets the local default to sts.amazonaws.com and coalesces at the call site, so a caller that forwards its own empty-defaulted input cannot reintroduce this. role-session-name and role-duration-seconds are deliberately left alone: the wrapped action declares no action.yml default for either, so their empty strings fall through to its internal defaults rather than overriding anything. Pinning values here would change behaviour instead of restoring it. --- .github/actions/aws-credentials/action.yml | 9 ++++++--- docs/actions/aws-credentials.md | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/actions/aws-credentials/action.yml b/.github/actions/aws-credentials/action.yml index 9675562..58310ad 100644 --- a/.github/actions/aws-credentials/action.yml +++ b/.github/actions/aws-credentials/action.yml @@ -14,9 +14,9 @@ inputs: required: false default: '' audience: - description: OIDC audience for the web identity token. + description: OIDC audience for the web identity token. Defaults to sts.amazonaws.com. required: false - default: '' + default: sts.amazonaws.com role-duration-seconds: description: Lifetime of the assumed-role credentials, in seconds. required: false @@ -42,7 +42,10 @@ runs: aws-region: ${{ inputs.aws-region }} role-to-assume: ${{ inputs.role-to-assume }} role-session-name: ${{ inputs.role-session-name }} - audience: ${{ inputs.audience }} + # Coalesced, not forwarded bare: an empty string overrides the wrapped action's own + # default rather than falling back to it, and an empty audience makes GitHub mint the + # token with aud=https://github.com/, which STS then rejects. + audience: ${{ inputs.audience || 'sts.amazonaws.com' }} role-duration-seconds: ${{ inputs.role-duration-seconds }} aws-access-key-id: ${{ inputs.aws-access-key-id }} aws-secret-access-key: ${{ inputs.aws-secret-access-key }} diff --git a/docs/actions/aws-credentials.md b/docs/actions/aws-credentials.md index 739bc4d..1095050 100644 --- a/docs/actions/aws-credentials.md +++ b/docs/actions/aws-credentials.md @@ -19,7 +19,7 @@ OIDC role assumption needs `permissions: id-token: write` on the job. | `aws-region` | required | AWS region to configure. | | `role-to-assume` | `''` | IAM role ARN to assume via OIDC. Leave blank when using static keys. | | `role-session-name` | `''` | Session name for the assumed role. | -| `audience` | `''` | OIDC audience for the web identity token. | +| `audience` | `sts.amazonaws.com` | OIDC audience for the web identity token. Leave unset unless your IAM OIDC provider expects a different audience. | | `role-duration-seconds` | `''` | Lifetime of the assumed-role credentials, in seconds. | | `aws-access-key-id` | `''` | Static access key ID, as an alternative to `role-to-assume`. | | `aws-secret-access-key` | `''` | Static secret access key. |