如何使用“npm login”设置npm凭据而不从stdin读取?

102

我正在尝试在Docker容器中自动化执行npm publish,但当npm login命令尝试读取用户名和电子邮件时,我收到了一个错误:

npm login << EOF
username
password
email
EOF

在Bash终端中可以正常运行,但在没有 stdin 的容器中会显示错误:

Username: Password: npm ERR! cb() never called!
npm ERR! not ok code 0
根据 npm-adduser 的说明:

用户名、密码和电子邮件将从提示中读取。

我如何在不使用 stdin 的情况下运行 npm login

你的命令对我来说确实有效,谢谢! :) 唯一缺少的是添加 --repository 参数... - dokaspar
我自己今天在.npmrc中使用了_authToken=${NPM_TOKEN},这在2014年之前是不可用的。感谢所有的回答,让我犹豫不决,不知道该选择哪一个作为最佳答案。 - undefined
17个回答

60
npm set //<registry>/:_authToken $TOKEN

Github Package Registry的示例:

npm set //npm.pkg.github.com/:_authToken $GITHUB_TOKEN

这是我发现的最简单的解决方案。


8
这是完美的解决方案。 - Beto Frega
1
注意GitHub Actions用户:密码可以通过${{ secrets.SECRET_NAME }}访问,因此您需要执行npm set "//registry.npmjs.org/:_authToken" ${{ secrets.NPM_TOKEN_OR_WHATEVER }} - Kipras Melnikovas
1
注意 Docker 用户:Docker 构建可能会将 NPM_TOKEN 的明文值记录到您的控制台/CI 日志中。您可以通过将此命令放入 ./npm-config.sh 并执行该命令来避免这种情况。 - Florian Wendelborn
1
我已经搜索了好几天了,谢谢! - rmartinus
_authToken 和 _auth 有什么区别? - Ravi Badoni
我在这方面很幸运。我只需要在TeamCity中的_authToken和$TOKEN之间添加=就可以了 [npm set //npm.pkg.github.com/:_authToken=$TOKEN] - undefined

55

TL;DR: 直接向注册表发送HTTP请求:

TOKEN=$(curl -s \
  -H "Accept: application/json" \
  -H "Content-Type:application/json" \
  -X PUT --data '{"name": "username_here", "password": "password_here"}' \
  http://your_registry/-/user/org.couchdb.user:username_here 2>&1 | grep -Po \
  '(?<="token": ")[^"]*')

npm set registry "http://your_registry"
npm set //your_registry/:_authToken $TOKEN

缘由

在幕后,npm adduser向注册表发出HTTP请求。不必强制adduser按您想要的方式运行,您可以直接向注册表发出请求而无需通过cli,然后使用npm set设置身份验证令牌。

源代码建议,您可以使用以下有效负载向http://your_registry/-/user/org.couchdb.user:your-username发出PUT请求

{
  name: username,
  password: password
}

这将在注册表中创建一个新用户。

非常感谢@shawnzhu找到了更清洁的方法来解决问题。


这很聪明,但在我的情况下似乎不起作用。我们正在使用ProGet,而该命令的输出似乎只返回像{ "ok": "true" }这样的对象,没有任何令牌。看起来ProGet改变了该命令的响应...如果我发现了什么,我会及时通知您。 - Marc-Andre R.
4
如果我们只想登录,而不是添加新用户怎么办?使用上述PUT请求会返回“用户已存在”。 - AntouanK
3
已找到解决方案:https://github.com/rlidwka/sinopia/issues/329#issuecomment-217406747 - AntouanK
7
如果您已经使用 npm login 登录过,那么令牌的信息可以在您家目录下的文件 ~/.npmrc 中找到。接着,只需运行 npm set //your-registry.com/:_authToken="wKaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=" 即可。请注意,这里的翻译保持原意,同时更通俗易懂。 - King Midas
2
有其他人遇到这个解决方案无法工作的问题吗?我总是收到“您没有发布权限”的消息。 - Highriser
显示剩余2条评论

16
这在我的一个DevOps流程中起作用了。
步骤:
1. 使用Shell生成基于Base 64的npm注册表凭据的_auth,以确保安全性。
    echo -n 'myuser:mypassword' | openssl base64
    Result will be something like : eWFob29vb2E=

在执行npm install ...之前,设置npm注册表的URL和_auth。
    npm config set registry https://nexus-acme.com/repository/npm-group/
    npm config set _auth eWFob29vb2E=

就这些了。你可以运行npm install,然后你的私有模块就会被下载下来。

注意

  • Node.js提供基本身份验证(用户:密码)作为有效选项:
  • 使用基本身份验证,基本令牌通常存储在~/.npmrc
  • 这个基本令牌使用base64编码,因此任何能访问您的机器的人都可以轻松解码并查看您的用户:密码。如果您担心此问题,请向Node.js的创建者询问为什么要使用这种方式。
  • 对于开发人员和DevOps流程来说,这种策略是常见的:maven(Java),pip(Python),nuget(C#),等等
  • 如果您担心在开发人员机器上存储秘密,您应该使用一些高级的npm软件包注册表,如GitHub,其中提供了一个带有过期时间的令牌。无论如何,这个令牌都应该存储在机器上,因此没有办法在不登录和/或不存储获得的令牌的情况下与NPM注册表建立稳定的连接
  • 一个疯狂的选择可能是使用每次npm安装都需要进行Web登录的oauth2。我无法想象一个DevOps自动化(纯shell)中,在每次npm安装时都需要人工系统管理员执行Web登录...
  • 对于非交互式流程,秘密始终需要在服务器或开发人员机器上。

这对我完全没用...我已经在我的.npmrc文件中设置了它,尝试npm install仍然出现身份验证错误。 - wdlax11
你的 Node.js 版本是多少?删除旧的 .npmrc 文件,然后再试一次! - JRichardsz
1
对我来说有效,可以在没有任何互联网访问的情况下登录到Nexus私有仓库。 - Sven Sönnichsen
FYI:Base64根本不是“安全”的,最多只能算是混淆,而且没有提供任何安全保障。 - Glenn 'devalias' Grant
当然我知道。也许你可以问一下NPM的创建者为什么要继续提供这种方式。你也可以使用2fa,但那不是问题所在。 - JRichardsz

16

npm-cli-login 能让你在无需STDIN的情况下登录NPM。

为了安装,请运行:

npm install -g npm-cli-login

使用示例:

npm-cli-login -u Username -p Password -e test@example.com -r https://your-private-registry-link

14

一个expect脚本对我很有用。您需要确保已安装expect,此命令适用于ubuntu:

apt-get install expect-dev

你的脚本可能看起来像这样(npm_login_expect):

#!/usr/bin/expect -f

# set our args into variables
set i 0; foreach n $argv {set "p[incr i]" $n}

set timeout 60
#npm login command, add whatever command-line args are necessary
spawn npm login
match_max 100000

expect "Username"    
send "$p1\r"

expect "Password"
send "$p2\r" 

expect "Email"
send "$p3\r"

expect {
   timeout      exit 1
   eof
}

然后像这样调用它:

expect -f npm_login_expect myuser mypassword "myuser@domain.com"

1
如果有tty可以被expect读取,则返回“是”。我的问题是,根本没有STDIN或sudo tty。 - shawnzhu
在厨师配方中也很好用。 - NikG
1
我遇到了错误:spawn npm login npm ERR! wrong # args: should be "eof channelId" while executing "eof" invoked from within "expect { timeout exit 1 eof }" (file "npm_login.sh" line 20) - was_777

14

我采取了稍微不同的方法,似乎仍然非常有效。首先,您需要一个身份验证令牌。这可以很容易地通过在本地运行npm adduser,然后从位于用户文件夹中的~/.npmrc中获取生成的令牌来获得。为了在CI服务器上通过身份验证,需要将此身份验证令牌附加到用户的.npmrc中的注册表URL中(类似于本地操作),而不是存储库中的.npmrc,所以这些非常适合作为CI配置中的脚本步骤。

- echo "//<npm-registry>:8080/:_authToken=$AUTH_TOKEN" > ~/.npmrc
- npm publish

在您的设置中,AUTH_TOKEN作为秘密变量存储。

测试的一种好方法是将npm publish替换为npm whoami以进行测试并确保成功登录。

这是我整个发布配置:

publish:
  stage: deploy
  only:
    - tags
  script:
    - yarn run build
    - echo "//<npm-registry>:8080/:_authToken=$NPME_AUTH_TOKEN" > ~/.npmrc
    - npm publish
    - echo 'Congrats on your publication!'

我正在使用gitlab-ci,但我认为这适用于任何ci应用程序。


npm ping 已经被精确地设置为允许测试连接和凭据。此外,请注意,有时将输出到本地的 .npmrc 文件中而不是始终放置在用户的 HOME 文件夹中是很有用的——特别是为了避免因运行某些自动化命令而破坏用户的日常配置。 - ErikE
谢谢 @bryson,我今天在 GitHub Actions 中使用了这个解决方案。 - undefined

8

一种解决方案是获取令牌并更新 ~/.npmrc 文件。

export ARTIFACTORY_TOKEN=`curl --silent --show-error --fail -u $ARTIFACTORY_USERNAME:$ARTIFACTORY_API_KEY https://artifactory.my.io/artifactory/api/npm/auth | \
grep -oP '_auth[\s?]=[\s?]\K(.*)$'`

echo "@my:registry=https://artifactory.my.io/artifactory/api/npm/npm-release-local/" > ~/.npmrc
echo "//artifactory.my.io/artifactory/api/npm/npm-release-local/:_auth=${ARTIFACTORY_TOKEN}" >> ~/.npmrc
echo "//artifactory.my.io/artifactory/api/npm/npm-release-local/:email=${ARTIFACTORY_USERNAME}" >> ~/.npmrc
echo "//artifactory.my.io/artifactory/api/npm/npm-release-local/:always-auth=true" >> ~/.npmrc

这可以防止从npmjs检索@scope软件包时出现问题。


6

npm login命令会将所有的凭据存储在全局的.npmrc文件中。该模式不是固定的,可能会发生变化。下面进行解释:

有两种模式,任意一种都可以使用。注意:npm可能以其他模式存储验证数据,因此最好在全局范围内交叉检查.npmrc文件的内容。

每次执行npm login时,如果.npmrc不存在,则会创建一个条目。

所以,唯一需要做的就是从全局的.npmrc复制该行并将其放入本地或项目的.npmrc中,然后从CI运行npm publish命令。不需要显式执行npm login


我不建议将实际的身份验证令牌保存在.npmrc文件中。使用环境变量,例如//your_registry/:_authToken=${NPM_AUTH_TOKEN}会更好。 - undefined

3

这是在Alexander F的答案基础上进一步完善的。这只是他提供代码的简化版本,与npm-registry-client提供的示例代码混合。

"use strict";

var RegClient = require('npm-registry-client')
var client = new RegClient()
var uri = "https://registry.npmjs.org/npm"
var params = {timeout: 1000}

var username = 'my.npm.username'
var password = 'myPassword'
var email = 'my.email@gmail.com'

var params = {
  auth: {
    username,
    password,
    email
  }
};

client.adduser(uri, params, function (error, data, raw, res) {
  if(error) {
    console.error(error);
    return;
  }
  console.log(`Login succeeded`);
  console.log(`data: ${JSON.stringify(data,null,2)}`);
  console.log(`NPM access token: ${data.token}`);
});

3

npm login 是互动的方法。因此,建议使用 npm-cli-login login

请按照以下步骤操作:

               1. npm install -g npm-cli-login
               2. npm-cli-login login -u username -p password -e abc@gmail.com -r http://registry.npmjs.org
               3. npm publish src --registry=http://registry.npmjs.org

我想要将模块发布到http://registry.npmjs.org,并且我已经用我的电子邮件地址(abc@gmail.com)用户名密码进行了注册。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接