Automated NPM secret rotation in GitHub Actions
NPM recently announced that all long-lived tokens are being revoked, and that going forwards any new tokens may be valid for a maximum of 90 days.
This presents a challenge for me, as I've built a system where when I tag a release on GitHub in my JavaScript projects, it builds and publishes to NPM using an access token. Rotating those tokens regularly would be a lot of toil.
Upgrading to OIDC
The correct way to solve this is to adopt trusted publishing (OIDC). However, I don't have time to update all of my projects right now. I'd like to keep using an access token until I have time to update each project.
Rotating GitHub Actions user secrets at scale
Fortunately, back in 2020 I was feeling the pain of rotating GitHub secrets for a user account as I couldn't use organization level secrets and built github-update-secret.
github-update-secret iterates over all of your repositories and checks if the provided secret name is set. If so, it updates the value to the new value provided.
How it works
- Provide GitHub authentication by populating the
GITHUB_TOKENenvironment variable, or passing the--patflag to the CLI - Fetch a list of all repos to which the authenticated user has admin access for the provided user/org
- Fetch the list of secrets on each repository
- For each repository that has a secret named the same as the provided
SECRET_NAME, update the value of that secret - Check if the provided target is an organisation.
- If so:
- Check if there is an org secret named
SECRET_NAME - If there is, update the value
- Check if there is an org secret named
Example
I just rotated all of my NPM_TOKEN secrets with a secret that's valid for another 90 days using the following command:
bashGITHUB_TOKEN=ghp_... DEBUG=github-update-secret npx github-update-secret <user/org> <SECRET_NAME> <new_value>
Running the tool with DEBUG=github-update-secret allows you to see all of the repositories that are being updated:
bash❯ DEBUG=github-update-secret npx github-update-secret mheap NPM_TOKEN npm_...github-update-secret Fetching repo list +0msgithub-update-secret Processed repo list +2sgithub-update-secret Building list of repos using [npm_token] +0msgithub-update-secret Found [27] repos with the secret [npm_token] +2sgithub-update-secret Updating action-guard +1msgithub-update-secret Updated action-guard +347msgithub-update-secret Updating action-router +1msgithub-update-secret Updated action-router +381msgithub-update-secret Updating action-run +1msgithub-update-secret Updated action-run +338msgithub-update-secret Updating actions-output-wrapper +0ms...snip...github-update-secret Check if provided user is an org +0msgithub-update-secret User is not an org. Skipping org secret update +142ms
NPM’s move to 90-day tokens is good for security but rough on existing workflows. OIDC is the long-term fix, but until I can update each project, github-update-secret gives me a quick way to rotate tokens across all my repos and keep releases moving. It’s not perfect, but it provides enough breathing room until I can do the real migration.