不知道你是不是在 yarn 或 npm install 的过程中出现下面的错误:
bash
error Error: https://npm.pkg.github.com/download/**/3a1155bbb6e4eea34e2e813e456e3a165ec74641: Request failed "401 Unauthorized"
at ResponseError.ExtendableBuiltin (/Users/cyrus/.nvm/versions/node/v20.19.3/lib/node_modules/yarn/lib/cli.js:696:66)
at new ResponseError (/Users/cyrus/.nvm/versions/node/v20.19.3/lib/node_modules/yarn/lib/cli.js:802:124)
at Request.<anonymous> (/Users/cyrus/.nvm/versions/node/v20.19.3/lib/node_modules/yarn/lib/cli.js:66750:16)
at Request.emit (node:events:524:28)
at module.exports.Request.onRequestResponse (/Users/cyrus/.nvm/versions/node/v20.19.3/lib/node_modules/yarn/lib/cli.js:142287:10)
at ClientRequest.emit (node:events:524:28)
at HTTPParser.parserOnIncomingClient (node:_http_client:702:27)
at HTTPParser.parserOnHeadersComplete (node:_http_common:118:17)
at TLSSocket.socketOnData (node:_http_client:544:22)
at TLSSocket.emit (node:events:524:28)
info Visit https://yarnpkg.com/en/docs/cli/install for documentation about this command.
从这个我们可以得到的信息就是在下载 https://npm.pkg.github.com/download/**/3a1155bbb6e4eea34e2e813e456e3a165ec74641 的时候出现了 401 的错误。
401 vs 403 代表什么
- 401 Unauthorized :像"你没带门禁卡 / 卡号不对"
常见原因:没配置 token、token 没被 npm/yarn 读取、token 拼错/为空、环境变量没生效。 - 403 Forbidden :像"你带了门禁卡,但你没权限进这栋楼"
常见原因:token 权限 scopes 不够、token 没授权 SSO、token 绑定了错误的组织/仓库范围、账号本身没权限读这个包。
为什么拉私有包需要"另一把钥匙"
很多同学会遇到这种困惑:我 git clone 用 SSH key 没问题(仓库代码拉得下来,说明我的身份已经认证成功了),但 yarn / npm install 却报 401/403,拉不下 @your-scope/* 这类私有包
原因是:拉代码(Git)和拉包(npm registry) 是两条完全不同的通道。
- Git(拉仓库代码) :常用 SSH key 或 HTTPS token,走的是
git@host:org/repo.git这一套协议。 - npm registry(拉依赖包) :走的是 HTTPS 请求
https://npm.pkg.github.com/...,需要 npm Token(PAT) 这把钥匙。
必备知识:.npmrc 是 npm/yarn 的"连接配置文件"
npm / yarn 会读取 .npmrc(可以是全局 ~/.npmrc,也可以是项目根目录 .npmrc),里面通常包含两类东西:
- 告诉它:某个 scope 的包去哪个 registry 下载
- 告诉它:访问该 registry 时用哪个 token
例如私有 scope 包 @your-scope/*:
properties
@your-scope:registry=https://npm.pkg.github.com
always-auth=true
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
把它类比成数据库连接:
registry=...类似DB_HOST_authToken=...类似DB_PASSWORDscope类似"不同业务库走不同连接串"
安全建议:不要把真实 token 写进 Git 仓库;更推荐用环境变量(如 ${NPM_TOKEN})或系统钥匙串(Keychain)管理。
到底怎样做才能拉取得到?
下面以 macOS + zsh +(可选)Keychain 为例。其他系统也适用:把"Keychain"替换成你们的安全存储方式即可。
Token 过期怎么做?
直接重复下面的 第 2 步(生成新 Token)+ 第 4.1 步(写入/覆盖 Keychain)+ 第 4.2 步(重新加载终端环境变量)+ 第 5 步(验证) 。
本质就是"换钥匙 → 放回钥匙串 → 让当前进程重新读一遍配置 → 自检"。
第 1 步:确认你要安装的私有包来自哪里
看 package.json 里是否有类似依赖:
@your-scope/some-private-package
如果有,就意味着安装依赖时会访问对应的私有 registry(例如 GitHub Packages:https://npm.pkg.github.com)。
第 2 步:生成可读私有包的 GitHub Token(PAT)
推荐优先使用 Tokens (classic) (兼容性最稳,最少坑):
在 GitHub 的 Token 页面生成 classic PAT,至少勾选:
read:packages(必须:读取私有 packages)- 如果 packages 对应的仓库是私有的,通常还需要:
-
repo(常见必须)
SSO(组织常见)
如果你的 GitHub 组织开启了 SSO,你还需要在 token 列表里对组织点:
- Authorize SSO
类比:token 是"钥匙",SSO Authorize 相当于"把钥匙登记到这栋楼的门禁系统里",不登记就会 403。
第 3 步:配置 .npmrc(推荐:全局 ~/.npmrc)
在你的 home 目录创建/更新 ~/.npmrc:
properties
@your-scope:registry=https://npm.pkg.github.com
always-auth=true
//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
注意:这里用 ${NPM_TOKEN} 是为了不把 token 明文落盘到仓库里。
我没有配置项目 .npmrc,为什么也能拉取?
这很常见,原因是 npm/yarn 的配置来源不止项目目录:
- 可能你已经在 全局
~/.npmrc里配过(所以项目里看不到) - 可能你曾经执行过
npm config set @your-scope:registry ...(它最终也会写入用户级配置) - 可能你团队用的是
.yarnrc或者某些开发工具帮你写过配置
你可以用这三条"探针"确认当前到底靠什么在工作(不会泄露 token):
properties
npm config get @your-scope:registry
echo "${NPM_TOKEN:+set}"
npm whoami --registry=https://npm.pkg.github.com
第 4 步:把 Token 放到环境变量(推荐:macOS Keychain + .zshrc 自动加载)
在 ~/.zshrc 里写类似这一句:
bash
# Load GitHub Packages token from macOS Keychain
export NPM_TOKEN="$(security find-generic-password -a "$USER" -s GITHUB_PACKAGES_NPM_TOKEN -w 2>/dev/null)"
它的意思是:
- 从 macOS 钥匙串(Keychain) 里读取一条"通用密码"
- 条件是
Account=$USER且Service=GITHUB_PACKAGES_NPM_TOKEN - 读取到的 password 作为
NPM_TOKEN导出给 npm/yarn 使用
也可以直接把 token 写入到 ~/.zshrc 类似的文件中,对于 windows 的用户可以配置环境变量来完成。
4.1 先把 token 存进 Keychain(推荐用命令,最准确)
把 NEW_TOKEN 换成你刚生成的 PAT(不要提交/不要分享给任何人):
bash
NEW_TOKEN="ghp_xxx..." # 只在本地填
security add-generic-password \
-a "$USER" \
-s "GITHUB_PACKAGES_NPM_TOKEN" \
-w "$NEW_TOKEN" \
-U \
~/Library/Keychains/login.keychain-db
unset NEW_TOKEN
类比:Keychain 像"本机密码保险箱",.zshrc 像"启动脚本",每次开终端就自动把保险箱里的 token 拿出来放到环境变量里。
4.2 重新打开一个终端,让 .zshrc 生效(或执行 source ~/.zshrc 类似的命令使其生效)
然后确认环境变量存在:
bash
echo "${NPM_TOKEN:+set}"
输出 set 就对了。
你可能会发现"刚写入 Keychain 但不立即生效",而是要新开终端 。这并不神秘:
Keychain 像数据库,.zshrc 像应用启动时读取配置的代码。你更新了"数据库里的 token",但当前终端进程里的 NPM_TOKEN 变量还是旧值,除非重新加载配置。
想在当前终端立刻生效,可以执行其一:
bash
source ~/.zshrc
# 或者手动重新 export 一次
export NPM_TOKEN="$(security find-generic-password -a "$USER" -s GITHUB_PACKAGES_NPM_TOKEN -w 2>/dev/null)"
第 5 步:验证鉴权是否通
这是最直观的验证(不会输出 token):
bash
npm whoami --registry=https://npm.pkg.github.com
- 能输出用户名:说明 token 生效、鉴权 OK
- 报 401/403:回到"401 vs 403"那节排查
第 6 步:安装依赖
bash
yarn
# 或
npm i