# 如何正確設定 Azure Artifacts 的 npm 套件來源 (Feeds)

如果你在 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 建立一個 npm Registry 讓前端團隊存取私有的 npm 套件，最先需要克服的問題就是要如何通過身分認證，但這部分的設定在 Windows 與在 Linux/macOS 設定步驟是不一樣的，這篇文章將說明如何正確的在 Windows 與 Linux/macOS 進行設定。

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/76646a7a05b464f82c10-47086034-7107-43ed-9f2b-a12ef37b3e13.webp)

### Windows

在 Windows 設定相對簡單很多，只要安裝一個 [vsts-npm-auth](https://www.npmjs.com/package/vsts-npm-auth) 工具，然後執行驗證程序即可，以下是需要設定的 3 個步驟。

1.  在專案根目錄加入一個 `.npmrc` 檔案
    
    如果所有套件都要透過 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 下載，直接拿 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 的 Feed 當作 Proxy 來用，那麼你可以這樣設定：
    
    ```
    registry=https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/
    always-auth=true
    ```
    
    > 注意: `ORGNAME` 與 `FEEDNAME` 是變數，請依照你的設定來修改！
    
    如果你的 npm 套件被定義在 [scoped package](https://docs.npmjs.com/cli/v10/using-npm/scope) 之下的話，假設你的 scoped package 是 `@miniasp`，那麼你可以這樣設定：
    
    ```
    registry=https://registry.npmjs.org/
    @miniasp:registry=https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/
    always-auth=true
    ```
    
    > 這樣的設定可以讓大部分 npm 套件都從 `registry.npmjs.org` 取得，只有公司內部的 npm 套件從 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 取得。
    
2.  安裝一個 [vsts-npm-auth](https://www.npmjs.com/package/vsts-npm-auth) 認證工具 (Windows only)
    
    這套 `vsts-npm-auth` 工具是專門設計給 Windows 使用的，雖然這是一個 npm 套件，但只有 Windows 可以正常執行喔！
    
    我們先安裝這套工具起來：
    
    ```bash
    npm install -g vsts-npm-auth
    ```
    
3.  執行驗證程序
    
    這套 `vsts-npm-auth` 工具會自動引導你到 Azure AD (Entra ID) 進行登入，而且也支援 MFA 多因素驗證，使用上還算簡單。
    
    使用方式如下，這裡的 `-config` 可以指定你的 `.npmrc` 檔案：
    
    ```bash
    vsts-npm-auth -config .npmrc
    ```
    
    執行時產生的訊息如下：
    
    ```
    vsts-npm-auth v0.42.1.0
    -----------------------
    Getting new credentials for source:https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/, scope:vso.packaging_write vso.drop_write
    ```
    
    > 這個執行過程並不會修改專案下的 `.npmrc` 檔案的內容，而是將**認證資訊**儲存在你的 `$HOME` 目錄下的 `~/.npmrc` 檔案中。
    
    > 注意：執行認證的過程會自動幫你在 Azure DevOps Service 建立一個 90 天效期的 PAT 權杖(Personal Access Token)，這個 PAT 的名稱是 `Packaging clients: https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/ on XXXXXXXX`，如果你跑很多次的話，就會建立很多份 PAT 權杖。
    
    > 這份 PAT 擁有的權限包含了 `vso.packaging_write` 與 `vso.drop_write` 這兩個權限，但我從官方的 [scopes.md](https://github.com/MicrosoftDocs/azure-devops-docs/blob/main/docs/integrate/includes/scopes.md) 文件中查不到 `vso.drop_write` 的說明，不過我很確定 `vso.packaging_write` 就是可以針對 Artifacts 套件庫進行**讀取**與**寫入**的權限。
    

## Linux / macOS / Container

在 non-Windows 平台設定也不是多麻煩，就是 PAT 要自己建立而已，以下是需要設定的 4 個步驟。

1.  在專案加入一個 `.npmrc` 檔案
    
    如果所有套件都要透過 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 下載，直接拿 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 的 Feed 當作 Proxy 來用，那麼你可以這樣設定：
    
    ```
    registry=https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/
    always-auth=true
    ```
    
    > 注意: `ORGNAME` 與 `FEEDNAME` 是變數，請依照你的設定來修改！
    
    如果你的 npm 套件被定義在 scoped package 之下的話，假設你的 scoped package 是 `@miniasp`，那麼你可以這樣設定：
    
    ```
    registry=https://registry.npmjs.org/
    @miniasp:registry=https://ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/
    always-auth=true
    ```
    
    > 這樣的設定可以讓大部分 npm 套件都從 `registry.npmjs.org` 取得，只有公司內部的 npm 套件從 [Azure Artifacts](https://azure.microsoft.com/zh-tw/products/devops/artifacts?WT.mc_id=DT-MVP-4015686) 取得。
    
2.  人工建立一個 PAT 權杖(Personal Access Token)
    
    建立 PAT 的時候，除了設定 `Name`, `Organization` 跟 `Expiration` 欄位外，最重要的就是選擇 `Scopes` 欄位，記得套用最小權限原則下去設定，並且選擇 `Packaging` 這個分類下的 Scope，其權限有 `Read`, `Read & write` 與 `Read, write, & manage`。
    
    > 建立完 PAT 權杖之後，要記得先把 PAT 權杖的內容複製起來，因為這個 PAT 權杖只會顯示一次，之後就無法再看到了！
    
3.  將以下內容加入 `~/.npmrc` 檔案中
    
    ```csharp
    ; begin auth token
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/:username=miniasp
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/registry/:email=npm requires email to be set but doesn't use the value
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/:username=miniasp
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/:_password=[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]
    //ORGNAME.pkgs.visualstudio.com/_packaging/FEEDNAME/npm/:email=npm requires email to be set but doesn't use the value
    ; end auth token
    ```
    
    > 上述的 `[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]` 是我們在下個步驟要替換掉的變數。
    
4.  將 PAT 先轉換成 Base64 編碼字串
    
    你可以使用 Linux 內建的 `base64` 命令列工具：
    
    ```bash
    echo 'ljb5cl745ljblllxa4gloj2qkzm44sie7naz7w46funigr6wb5lq' | base64
    ```
    
    也可以透過 Node.js 來幫我們轉成 Base64 編碼字串：
    
    ```bash
    node -e "require('readline') .createInterface({input:process.stdin,output:process.stdout,historySize:0}) .question('PAT> ',p => { b64=Buffer.from(p.trim()).toString('base64');console.log(b64);process.exit(); })"
    ```
    
    執行上述命令後會看到 `PAT>` 提示字元，貼上 `PAT` 並按下 Enter 鍵，就會看到 PAT 的 Base64 編碼結果，請將這個結果取代掉 `~/.npmrc` 檔案中的 `[BASE64_ENCODED_PERSONAL_ACCESS_TOKEN]` 變數。
    
    > 我不建議你將這些密碼寫在專案的 `.npmrc` 檔案中，因為你很有可能會意外的將這些憑證資訊加入到 Git 版控裡，那就不是很理想了。
    

### 相關連結

-   [Set up your client's npmrc - Azure Artifacts | Microsoft Learn](https://learn.microsoft.com/en-us/azure/devops/artifacts/npm/npmrc?view=azure-devops&WT.mc_id=DT-MVP-4015686&tabs=windows%2Cclassic)
-   [vsts-npm-auth - npm](https://www.npmjs.com/package/vsts-npm-auth)
-   [如何正確設定 Azure Artifacts 的 NuGet 套件來源 (Feeds)](/post/2019/01/16/Setup-Azure-Artifacts-NuGet-Feeds)
-   [npmrc | npm Docs](https://docs.npmjs.com/cli/v9/configuring-npm/npmrc?v=true)
