Skip to content

Commit ca7764c

Browse files
committed
Switch NuGet publishing to Trusted Publishing (OIDC)
Replaces the long-lived NUGET_API_KEY secret with a short-lived token exchanged at publish time via NuGet/login@v1, scoped to the nuget-release environment so only an approved run can ever obtain one.
1 parent ddcff97 commit ca7764c

2 files changed

Lines changed: 32 additions & 10 deletions

File tree

.github/workflows/release.yml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,17 @@ jobs:
4949
path: ./artifacts/*.nupkg
5050

5151
# Requires manual approval before this job runs: configure a GitHub Environment named
52-
# "nuget-release" (Settings > Environments) with required reviewers, and add NUGET_API_KEY as a
53-
# secret scoped to that environment (not a repo-wide secret) so the key is only ever readable
54-
# after someone approves. Until that environment is configured, this job has no reviewers
55-
# assigned and nothing will publish - that's the intended state until this is explicitly set up.
52+
# "nuget-release" (Settings > Environments) with required reviewers. Publishing uses NuGet
53+
# Trusted Publishing (OIDC) - no NUGET_API_KEY secret needed. The matching trusted publishing
54+
# policy on nuget.org must have Repository Owner=Monnify, Repository=monnify-dotnet-lib,
55+
# Workflow File=release.yml, Environment=nuget-release, so a token is only trusted if it came
56+
# from a run of this exact workflow that passed through this exact (approval-gated) environment.
5657
publish:
5758
needs: build-test-pack
5859
runs-on: ubuntu-latest
5960
environment: nuget-release
61+
permissions:
62+
id-token: write # required to request the GitHub OIDC token for NuGet Trusted Publishing
6063
steps:
6164
- uses: actions/download-artifact@v4
6265
with:
@@ -67,8 +70,17 @@ jobs:
6770
with:
6871
dotnet-version: "8.0.x"
6972

73+
# Exchanges the GitHub OIDC token for a short-lived (~1hr) NuGet API key - nothing
74+
# long-lived is stored as a secret. NUGET_USER is just the nuget.org profile username
75+
# (not the account email), kept as a secret only so it's not hardcoded here.
76+
- name: NuGet login (OIDC -> temp API key)
77+
uses: NuGet/login@v1
78+
id: nuget_login
79+
with:
80+
user: ${{ secrets.NUGET_USER }}
81+
7082
- name: Push to NuGet.org
71-
run: dotnet nuget push "./artifacts/*.nupkg" --api-key "${{ secrets.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
83+
run: dotnet nuget push "./artifacts/*.nupkg" --api-key "${{ steps.nuget_login.outputs.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate
7284

7385
- name: Create GitHub Release
7486
uses: softprops/action-gh-release@v2

CONTRIBUTING.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,18 @@ Pushing a `vX.Y.Z` tag triggers [release.yml](.github/workflows/release.yml),
4343
which builds, tests, and packs, then **requires manual approval** before
4444
publishing: the publish job runs under a `nuget-release` GitHub Environment.
4545
Before the first release, that environment needs to be created (Settings →
46-
Environments) with required reviewers, plus a `NUGET_API_KEY` secret scoped
47-
to that environment specifically — not a repo-wide secret, so the key is only
48-
ever readable after someone approves. Until that's configured, the publish
49-
job has no reviewers and nothing will publish, which is the intended state
50-
until this is deliberately set up.
46+
Environments) with required reviewers.
47+
48+
Publishing itself uses [NuGet Trusted Publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing)
49+
(OIDC) rather than a stored API key — the workflow exchanges a short-lived
50+
GitHub token for a temporary (~1hr) NuGet API key at publish time, so there's
51+
no long-lived secret to leak or rotate. This requires a matching policy on
52+
nuget.org (your account → Trusted Publishing) with Repository Owner=`Monnify`,
53+
Repository=`monnify-dotnet-lib`, Workflow File=`release.yml`,
54+
Environment=`nuget-release` — the environment restriction means a token is
55+
only trusted if it came from a run that passed through that approval gate.
56+
The only secret still needed is `NUGET_USER` (the nuget.org profile username,
57+
not the account email), scoped to the `nuget-release` environment. Until the
58+
environment and the nuget.org policy both exist, the publish job has no
59+
reviewers and nothing will publish, which is the intended state until this is
60+
deliberately set up.

0 commit comments

Comments
 (0)