Publishing npm packages with service accounts

Update: In October 2020 NPM added the option to generate Automation tokens, which bypass 2FA even for accounts that have it enabled for publication. Using service accounts still improves security by reducing the number of packages a successful attacker gets access to, so the below information may still be useful to you.

Since October 2017, npm has supported 2-factor authentication (2FA). This is good for security, and I've enabled it for both authorisation and publication on my main account, but it's difficult to use if you want to publish packages automatically from CI (e.g. using Travis CI):

npm ERR! publish Failed PUT 401
npm ERR! code E401
npm ERR! This operation requires a one-time password from your authenticator.
npm ERR! You can provide a one-time password by passing --otp=<code> to the command you ran.
npm ERR! If you already provided a one-time password then it is likely that you either typoed
npm ERR! it, or it timed out. Please try again.

I have found one proof of concept method using Hashicorp's Vault to provide a TOTP code as needed, but this requires a lot of external setup if you're not already using Vault as part of your workflow.

Instead, I've set up a second, "service" account to use when publishing from CI. Here's how:

  1. First, you need to create a new npm user to act as the service user for automated deploys. Unfortunately, npm doesn't currently give you a way to add a description to a user, but I've set the service user up with the same GitHub and Twitter handles as my main account, so it's hopefully clear who it belongs to. I've also given it a spiffy robot avatar via Gravatar. Enable 2FA, but make sure it's only for Authorization, not Authorization and Publishing.

  2. Next, create a token for use in the CI environment. Switch to the Tokens tab (or navigate directly to https://www.npmjs.com/settings/{user}/tokens) and click Create New Token. This will need to be set to the Read and Publish level.

  3. Finally, log in with your main account, visit the package you want to give the service account access to and switch to the Admin tab (or navigate directly to https://www.npmjs.com/package/{package}/access). In the Invite maintainer section, type the name of your service account and click Invite. You should then see something like this:

    fauxauth maintainers list

You can use the service account token in your CI environment for automated deploys, without needing to reduce the security of your main account. I'd recommend creating one service account per package so a breach doesn't impact multiple different projects (and, as always, using a password manager to create long, secure passwords that aren't reused).

One major downside is that you can't enable the "Require Two Factor Authentication to publish or modify settings" option for your package while you're using non-2FA service accounts. If you have other maintainers, make sure they are using full 2FA on their main accounts.

Comments !