NuGet Trusted Publishing: Making Secure Package Publishing Super Easy

Here I was, about to push my maui-version CLI tool to NuGet and needed to set up publishing from my GitHub Actions workflow. My first instinct was to create an API key, store it as a secret, and move on, like I always did. Set a reminder in some time to rotate the key, always a hassle, but such is life. But then I stumbled upon NuGet’s new Trusted Publishing feature and figured I’d give it a shot. Turns out, it was not only easy to implement but also way more secure than using long-lived API keys. Let me walk you through how it works and how to get it set up!
What is NuGet Trusted Publishing? #
Before we dive into the setup, let’s understand what we’re dealing with here. NuGet Trusted Publishing uses OpenID Connect (OIDC) to generate short-lived tokens directly from your CI/CD system (in our case, GitHub Actions). Instead of storing a long-lived API key that needs to be rotated and protected, your workflow gets a temporary key that expires in about an hour. This is significantly safer because there’s no secret to leak or rotate manually.
Think of it like this: instead of giving someone a master key to your house that they keep forever, you give them a key that only works for one hour and only for the specific task they need to do.
Learn more about it on the Microsoft Learn page about it.
Setting Up Trusted Publishing #
Alright, let’s get to the good stuff. Here are the steps to set this up for your NuGet package:
Step 1: Create a Trusted Publishing Policy #
Head over to nuget.org and sign in to your account. Click on your user menu in the top right corner and look for the Trusted Publishing option. You should see a screen like this:

Click on “Add a new trusted publisher” and you’ll be presented with a form. Fill in the following details:
- Policy Name: A name to remember this Trusted Publishing policy by. I chose “MauiVersionCLI” because its very specific to that NuGet package/tool
- Package Owner: This is you or your organization (on NuGet), just choose the right one from the dropdown
- Repository Owner: Your GitHub username or organization, this should match the repository that the code is under (e.g., jfversluis in my case)
- Repository: The name of your repository (e.g., maui-version in my case)
- Workflow File: The filename of your GitHub Actions workflow that publishes the package (e.g., publish.yml)
- Environment: Leave this empty unless you’re using a specific GitHub environment
That’s it! NuGet will now recognize your GitHub Actions workflow as a trusted publisher for your packages.
Step 2: Update Your GitHub Actions Workflow #
Now we need to update your workflow file to use Trusted Publishing. Here’s what you need to add:
First, set the permissions for your job:
permissions:
id-token: write # This enables GitHub OIDC token generation
Then add the NuGet login step before your publish step:
- name: NuGet login (OIDC - temp API key)
uses: NuGet/login@v1
with:
user: your-nuget-username
The your-nuget-username should be your NuGet.org username (your profile name, not your email). This step will handle the OIDC token exchange and give you a temporary API key that your workflow can use.
Step 3: Update Your Push Command #
Finally, update your dotnet nuget push command to use the temporary API key from the login step:
- name: Push to NuGet
run: dotnet nuget push ./bin/Release/*.nupkg --source https://api.nuget.org/v3/index.json
Wait, where is the handling of the API key?! The beauty of this is that the NuGet/login action handles everything. You don’t need to explicitly pass the API key anymore. It’s automatically used by the subsequent dotnet commands.
A Complete Example #
Here’s what a complete workflow might look like:
name: Publish to NuGet
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write # Required for OIDC
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Build
run: dotnet build --configuration Release
- name: Pack
run: dotnet pack --configuration Release --output ./bin/Release
- name: NuGet login (OIDC - temp API key)
uses: NuGet/login@v1
with:
user: your-nuget-username
- name: Push to NuGet
run: dotnet nuget push ./bin/Release/*.nupkg --source https://api.nuget.org/v3/index.json
Why This is Better #
You might be wondering why you’d go through this extra step when you could just use an API key. Here’s the thing: security. With Trusted Publishing, you don’t have to store any secrets in your repository or GitHub. There’s no API key lying around that could be accidentally exposed. The OIDC token is automatically issued by GitHub and is only valid for that specific workflow run. After about an hour, it expires and becomes useless.
Plus, nuget.org validates that the token came from the exact repository and workflow you specified. This means that even if someone compromises your workflow file, they can’t use this setup to publish packages from a different repository.
One Important Note #
Make sure that after you set up Trusted Publishing, you remove any old NuGet API keys from your GitHub secrets. If you have both an API key stored and Trusted Publishing set up, the old key might interfere with the process. Clear it out completely.
The Result #
That’s it! I was genuinely surprised at how straightforward this was to implement. My maui-version CLI tool now publishes to NuGet using Trusted Publishing, and I don’t have to worry about managing API keys anymore. If you’re planning to publish a NuGet package from GitHub Actions, I’d highly recommend giving this a try.