Publish private NPM packages securely with scopes on CodeArtifact
Published: 2021-12-13 16:33:07

NPM is a package manager configured by default to use the public registry but what if your code is proprietary and you want full disclosure? The easiest way is to use any cloud services (*that support private registries*) like Github, Amazon Web Services, or Google Cloud.
With just a few clicks you can setup a private registry for free. In this example, I'll be using CodeArtifact, I'm not affiliated in any way with Amazon Web Services, I had an account and didn't want to waste time trying other services. 

Disclaimer: I realize that people are dangerously messing up their environments and the most accurate way to prevent your modules from pushing to wrong servers is using NPM's scope packages. https://docs.npmjs.com/cli/v7/using-npm/scope

## Install AWS CLI Version 2

Pushing packages to CodeArtifact requires AWS authentication using the official CLI from Amazon, just run the following 3 commands to install it.

**Linux**

1. `curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"`
1. `unzip awscliv2.zip`
1. `./aws/install # as root`

**macOS**
1. `curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"`
1. `sudo installer -pkg AWSCLIV2.pkg -target /`

Output:
```
# /usr/local/bin/aws --version
aws-cli/2.2.47 Python/3.8.8 Linux/5.10.0-8-amd64 exe/x86_64.debian prompt/off

```

## Setup an IAM user (API Keys)


1. Go to IAM https://console.aws.amazon.com/iamv2/home#/users
1. Click `Add users`
1. Select `Access Key` as credential type
1. Create new group and give `AWSCodeArtifactAdminAccess` permissions.
1. Write down the generated `Access key ID` & `Secret access key`.

## Login using the generated keys
Running `aws configure` will setup the AWS (needed for generating the NPM token later).

**Don't forget to choose the default region. In this example I'm using Ireland EU.


Output:
```
aws configure

AWS Access Key ID [None]: ----------------
AWS Secret Access Key [None]: ------------
Default region name [None]: eu-west-1
Default output format [None]: json
```

## Create a new NPM scope repo
1. Login to the AWS dashboard.
1. Go to **CodeArtifact** and click `Create repository`. To avoid collisions give the scope repo a unique name like `ca-priv-repo-01`
1. Follow the instructions from AWS.

## Publish & Pull the modules 
Here comes the hard part. The most valid & secure way to configure any project, is using scopes (namespaces).
Publishing and pulling packages securely without messing up with global configuration  files which can lead to massive data leaks & terrible headaches. Thank to NPM's scopes, AWS will play fair with the package manager.

Tip: Check the dummy project here https://github.com/crafter999/private-npm-package

## Publish
1. Create a fresh package using the AWS repository name as an NPM scope. For example `@ca-priv-repo-01/mypackage`. Example package.json: `"name": "@ca-priv-repo-01/private-npm-package"` 
1. Go to the CodeArtifact panel.
1. Click on the target repository & then `View connection instructions`.
1. From there, select the `npm` package manager.
1. Choose `Manual Setup`. (Skip `Recommended setup` for the obvious reasons)
1. Run the first generated command on a terminal to export the CodeArtifact token.  
    ```
    Example: 

    export CODEARTIFACT_AUTH_TOKEN=`aws codeartifact get-authorization-token --domain mydomain --domain-owner 00000000 --query authorizationToken --output text`
    ```
1. Copy paste the 3 lines to the `.npmrc` replacing `registry=` with `@myscope:registry`
    ```
    Example: 
    @ca-priv-repo-01:registry=https://-----------.amazonaws.com/npm/ca-priv-repo-01/
    //-----------.amazonaws.com/npm/ca-priv-repo-01/:always-auth=true
    //-----------.amazonaws.com/npm/ca-priv-repo-01/:_authToken=${CODEARTIFACT_AUTH_TOKEN}
    ```
1. Add  the following lines to the `package.json` replacing `---id---` with te registry name.
    ```json
      "publishConfig": {
        "registry": "https://---id---.codeartifact.eu-west-1.amazonaws.com/npm/ca-priv-repo-01/"
      },
    ```
1. Lastly, upload any package using `npm publish`.

NOTE 1: If you're getting authentication errors like the following one, make sure you successfully exported the `CODEARTIFACT_AUTH_TOKEN`.
```
npm ERR! code E401
npm ERR! Unable to authenticate, your authentication token seems to be invalid.
npm ERR! To correct this please trying logging in again with:
npm ERR! npm login
```

NOTE 2: If you're getting domain errors like the following, make sure you are on the right region.
```
An error occurred (ResourceNotFoundException) when calling the GetAuthorizationToken operation: Domain not found. Domain 'example' owned by account '0000000000' does not exist.
```

## Pull
1. Create a new project.
1. Copy paste the `.npmrc` from the previous step to root folder of your project.
1. Export the token. Use the generated command from Connection Instructions (Manual Setup).
1. Install using `npm install @myscope/mypackage`.

## Example Code
You can find a dummy project here. https://github.com/crafter999/private-npm-package

## Support
If you liked this article please follow me on Twitter, it's free. https://twitter.com/devcrafter91