From 673b0b941111c273beb41ae2e4d7764706d750e6 Mon Sep 17 00:00:00 2001 From: Blind-Striker Date: Wed, 29 Jul 2026 14:52:12 +0300 Subject: [PATCH] ci: publish to NuGet.org via Trusted Publishing instead of an API key The 2.0.1 publish failed with 403 "The specified API key is invalid, has expired, or does not have permission" - NUGET_API_KEY aged out. Last successful publish was 2025-07-25 and nuget.org caps API keys at 365 days, so it expired almost to the day. nuget.org now also marks API keys "Not recommended" and steers key creation towards Trusted Publishing, so rotating was not the obvious path anyway. Trusted Publishing exchanges a GitHub OIDC token for a short-lived nuget.org key, which removes the whole class of failure: nothing to rotate, store or leak. This is the second credential of that vintage to die silently in this repo - GIST_SECRET has been returning 401 for the badge pipeline since the same week. The job gains id-token: write, a NuGet/login@v1 step runs immediately before the push (the temporary key is valid for one hour), and its output replaces the secret in the existing --package-secret argument. The Cake task is unchanged, and it already pushes to https://api.nuget.org/v3/index.json. The GitHub Packages path still uses GITHUB_TOKEN and is untouched. Requires the trusted publishing policy on nuget.org: package owner LocalStack.NET, repository localstack-dotnet/localstack-dotnet-client, workflow publish-nuget.yml, no environment. That policy is active. --- .github/workflows/publish-nuget.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 8ce393f..5626faa 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -41,6 +41,7 @@ jobs: permissions: contents: read packages: write + id-token: write # OIDC token for NuGet.org Trusted Publishing steps: - name: "Checkout" @@ -112,6 +113,17 @@ jobs: --package-version ${{ github.event.inputs.package-version }} \ --package-secret ${{ secrets.GITHUB_TOKEN }} + # Trusted Publishing: exchange a GitHub OIDC token for a short-lived NuGet.org key, so there is no + # long-lived NUGET_API_KEY to rotate or leak. The key lives one hour, hence immediately before the + # push rather than at the top of the job. + # Policy: owner LocalStack.NET, repo localstack-dotnet/localstack-dotnet-client, workflow publish-nuget.yml. + - name: "NuGet.org login (OIDC)" + id: nuget-login + if: ${{ github.event.inputs.package-source == 'nuget' }} + uses: NuGet/login@v1 + with: + user: localstack-dotnet + - name: "Publish to NuGet.org" if: ${{ github.event.inputs.package-source == 'nuget' }} run: | @@ -119,7 +131,7 @@ jobs: --package-source nuget \ --package-id ${{ github.event.inputs.package-id }} \ --package-version ${{ github.event.inputs.package-version }} \ - --package-secret ${{ secrets.NUGET_API_KEY }} + --package-secret ${{ steps.nuget-login.outputs.NUGET_API_KEY }} - name: "Upload Package Artifacts" uses: actions/upload-artifact@v7