Skip to content

Commit 84823a5

Browse files
committed
feat: include regKey and authKey in auth object
This will allow consumers to know *what* auth value provided the auth
1 parent 92ec0da commit 84823a5

File tree

2 files changed

+94
-14
lines changed

2 files changed

+94
-14
lines changed

lib/auth.js

Lines changed: 50 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const npa = require('npm-package-arg')
44
const { URL } = require('url')
55

66
// Find the longest registry key that is used for some kind of auth
7-
// in the options.
8-
const regKeyFromURI = (uri, opts) => {
7+
// in the options. Returns the registry key and the auth config.
8+
const regFromURI = (uri, opts) => {
99
const parsed = new URL(uri)
1010
// try to find a config key indicating we have auth for this registry
1111
// can be one of :_authToken, :_auth, :_password and :username, or
@@ -14,23 +14,40 @@ const regKeyFromURI = (uri, opts) => {
1414
// stopping when we reach '//'.
1515
let regKey = `//${parsed.host}${parsed.pathname}`
1616
while (regKey.length > '//'.length) {
17+
const authKey = hasAuth(regKey, opts)
1718
// got some auth for this URI
18-
if (hasAuth(regKey, opts)) {
19-
return regKey
19+
if (authKey) {
20+
return { regKey, authKey }
2021
}
2122

2223
// can be either //host/some/path/:_auth or //host/some/path:_auth
2324
// walk up by removing EITHER what's after the slash OR the slash itself
2425
regKey = regKey.replace(/([^/]+|\/)$/, '')
2526
}
27+
return { regKey: false, authKey: null }
2628
}
2729

28-
const hasAuth = (regKey, opts) => (
29-
opts[`${regKey}:_authToken`] ||
30-
opts[`${regKey}:_auth`] ||
31-
opts[`${regKey}:username`] && opts[`${regKey}:_password`] ||
32-
opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]
33-
)
30+
// Not only do we want to know if there is auth, but if we are calling `npm
31+
// logout` we want to know what config value specifically provided it. This is
32+
// so we can look up where the config came from to delete it (i.e. user vs
33+
// project)
34+
const hasAuth = (regKey, opts) => {
35+
if (opts[`${regKey}:_authToken`]) {
36+
return '_authToken'
37+
}
38+
if (opts[`${regKey}:_auth`]) {
39+
return '_auth'
40+
}
41+
if (opts[`${regKey}:username`] && opts[`${regKey}:_password`]) {
42+
// 'password' can be inferred to also be present
43+
return 'username'
44+
}
45+
if (opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]) {
46+
// 'keyfile' can be inferred to also be present
47+
return 'certfile'
48+
}
49+
return false
50+
}
3451

3552
const sameHost = (a, b) => {
3653
const parsedA = new URL(a)
@@ -63,11 +80,14 @@ const getAuth = (uri, opts = {}) => {
6380
if (!uri) {
6481
throw new Error('URI is required')
6582
}
66-
const regKey = regKeyFromURI(uri, forceAuth || opts)
83+
const { regKey, authKey } = regFromURI(uri, forceAuth || opts)
6784

6885
// we are only allowed to use what's in forceAuth if specified
6986
if (forceAuth && !regKey) {
7087
return new Auth({
88+
// if we force auth we don't want to refer back to anything in config
89+
regKey: false,
90+
authKey: null,
7191
scopeAuthKey: null,
7292
token: forceAuth._authToken || forceAuth.token,
7393
username: forceAuth.username,
@@ -88,8 +108,8 @@ const getAuth = (uri, opts = {}) => {
88108
// registry where we logged in, but the same auth SHOULD be sent
89109
// to that artifact host, then we track where it was coming in from,
90110
// and warn the user if we get a 4xx error on it.
91-
const scopeAuthKey = regKeyFromURI(registry, opts)
92-
return new Auth({ scopeAuthKey })
111+
const { regKey: scopeAuthKey, authKey: _authKey } = regFromURI(registry, opts)
112+
return new Auth({ scopeAuthKey, regKey: scopeAuthKey, authKey: _authKey })
93113
}
94114
}
95115

@@ -104,6 +124,8 @@ const getAuth = (uri, opts = {}) => {
104124

105125
return new Auth({
106126
scopeAuthKey: null,
127+
regKey,
128+
authKey,
107129
token,
108130
auth,
109131
username,
@@ -114,8 +136,22 @@ const getAuth = (uri, opts = {}) => {
114136
}
115137

116138
class Auth {
117-
constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) {
139+
constructor ({
140+
token,
141+
auth,
142+
username,
143+
password,
144+
scopeAuthKey,
145+
certfile,
146+
keyfile,
147+
regKey,
148+
authKey,
149+
}) {
150+
// same as regKey but only present for scoped auth. Should have been named scopeRegKey
118151
this.scopeAuthKey = scopeAuthKey
152+
// `${regKey}:${authKey}` will get you back to the auth config that gave us auth
153+
this.regKey = regKey
154+
this.authKey = authKey
119155
this.token = null
120156
this.auth = null
121157
this.isBasicAuth = false

test/auth.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ t.test('basic auth', t => {
3030
const gotAuth = getAuth(config.registry, config)
3131
t.same(gotAuth, {
3232
scopeAuthKey: null,
33+
regKey: '//my.custom.registry/here/',
34+
authKey: 'username',
3335
token: null,
3436
isBasicAuth: true,
3537
auth: Buffer.from('user:pass').toString('base64'),
@@ -61,6 +63,8 @@ t.test('token auth', t => {
6163
}
6264
t.same(getAuth(`${config.registry}/foo/-/foo.tgz`, config), {
6365
scopeAuthKey: null,
66+
regKey: '//my.custom.registry/here/',
67+
authKey: '_authToken',
6468
isBasicAuth: false,
6569
token: 'c0ffee',
6670
auth: null,
@@ -107,6 +111,8 @@ t.test('forceAuth', t => {
107111
}
108112
t.same(getAuth(config.registry, config), {
109113
scopeAuthKey: null,
114+
regKey: false,
115+
authKey: null,
110116
token: null,
111117
isBasicAuth: true,
112118
auth: Buffer.from('user:pass').toString('base64'),
@@ -140,6 +146,8 @@ t.test('forceAuth token', t => {
140146
}
141147
t.same(getAuth(config.registry, config), {
142148
scopeAuthKey: null,
149+
regKey: false,
150+
authKey: null,
143151
isBasicAuth: false,
144152
token: 'cafebad',
145153
auth: null,
@@ -168,6 +176,8 @@ t.test('_auth auth', t => {
168176
}
169177
t.same(getAuth(`${config.registry}/asdf/foo/bar/baz`, config), {
170178
scopeAuthKey: null,
179+
regKey: '//my.custom.registry/here/',
180+
authKey: '_auth',
171181
token: null,
172182
isBasicAuth: false,
173183
auth: 'c0ffee',
@@ -195,6 +205,8 @@ t.test('_auth username:pass auth', t => {
195205
}
196206
t.same(getAuth(config.registry, config), {
197207
scopeAuthKey: null,
208+
regKey: '//my.custom.registry/here/',
209+
authKey: '_auth',
198210
token: null,
199211
isBasicAuth: false,
200212
auth: auth,
@@ -246,6 +258,8 @@ t.test('globally-configured auth', t => {
246258
}
247259
t.same(getAuth(basicConfig.registry, basicConfig), {
248260
scopeAuthKey: null,
261+
regKey: '//different.registry/',
262+
authKey: 'username',
249263
token: null,
250264
isBasicAuth: true,
251265
auth: Buffer.from('globaluser:globalpass').toString('base64'),
@@ -261,6 +275,8 @@ t.test('globally-configured auth', t => {
261275
}
262276
t.same(getAuth(tokenConfig.registry, tokenConfig), {
263277
scopeAuthKey: null,
278+
regKey: '//different.registry/',
279+
authKey: '_authToken',
264280
token: 'deadbeef',
265281
isBasicAuth: false,
266282
auth: null,
@@ -276,6 +292,8 @@ t.test('globally-configured auth', t => {
276292
}
277293
t.same(getAuth(`${_authConfig.registry}/foo`, _authConfig), {
278294
scopeAuthKey: null,
295+
regKey: '//different.registry',
296+
authKey: '_auth',
279297
token: null,
280298
isBasicAuth: false,
281299
auth: 'deadbeef',
@@ -296,6 +314,8 @@ t.test('otp token passed through', t => {
296314
}
297315
t.same(getAuth(config.registry, config), {
298316
scopeAuthKey: null,
317+
regKey: '//my.custom.registry/here/',
318+
authKey: '_authToken',
299319
token: 'c0ffee',
300320
isBasicAuth: false,
301321
auth: null,
@@ -365,6 +385,8 @@ t.test('always-auth', t => {
365385
}
366386
t.same(getAuth(config.registry, config), {
367387
scopeAuthKey: null,
388+
regKey: '//my.custom.registry/here/',
389+
authKey: '_authToken',
368390
token: 'c0ffee',
369391
isBasicAuth: false,
370392
auth: null,
@@ -399,6 +421,8 @@ t.test('scope-based auth', t => {
399421
}
400422
t.same(getAuth(config['@myscope:registry'], config), {
401423
scopeAuthKey: null,
424+
regKey: '//my.custom.registry/here/',
425+
authKey: '_authToken',
402426
auth: null,
403427
isBasicAuth: false,
404428
token: 'c0ffee',
@@ -407,6 +431,8 @@ t.test('scope-based auth', t => {
407431
}, 'correct auth token picked out')
408432
t.same(getAuth(config['@myscope:registry'], config), {
409433
scopeAuthKey: null,
434+
regKey: '//my.custom.registry/here/',
435+
authKey: '_authToken',
410436
auth: null,
411437
isBasicAuth: false,
412438
token: 'c0ffee',
@@ -446,6 +472,8 @@ t.test('certfile and keyfile errors', t => {
446472
'//my.custom.registry/here/:keyfile': `${dir}/nosuch.key`,
447473
}), {
448474
scopeAuthKey: null,
475+
regKey: '//my.custom.registry/here/',
476+
authKey: 'certfile',
449477
auth: null,
450478
isBasicAuth: false,
451479
token: null,
@@ -479,6 +507,8 @@ t.test('do not be thrown by other weird configs', t => {
479507
const auth = getAuth(uri, opts)
480508
t.same(auth, {
481509
scopeAuthKey: null,
510+
regKey: '//localhost:15443/foo',
511+
authKey: '_authToken',
482512
token: 'correct bearer token',
483513
isBasicAuth: false,
484514
auth: null,
@@ -499,6 +529,8 @@ t.test('scopeAuthKey tests', t => {
499529

500530
t.same(getAuth(uri, { ...opts, spec: '@scope/foo@latest' }), {
501531
scopeAuthKey: '//scope-host.com/',
532+
regKey: '//scope-host.com/',
533+
authKey: '_authToken',
502534
auth: null,
503535
isBasicAuth: false,
504536
token: null,
@@ -508,6 +540,8 @@ t.test('scopeAuthKey tests', t => {
508540

509541
t.same(getAuth(uri, { ...opts, spec: 'foo@npm:@scope/foo@latest' }), {
510542
scopeAuthKey: '//scope-host.com/',
543+
regKey: '//scope-host.com/',
544+
authKey: '_authToken',
511545
auth: null,
512546
isBasicAuth: false,
513547
token: null,
@@ -517,6 +551,8 @@ t.test('scopeAuthKey tests', t => {
517551

518552
t.same(getAuth(uri, { ...opts, spec: '@other-scope/foo@npm:@scope/foo@latest' }), {
519553
scopeAuthKey: '//scope-host.com/',
554+
regKey: '//scope-host.com/',
555+
authKey: '_authToken',
520556
auth: null,
521557
isBasicAuth: false,
522558
token: null,
@@ -526,6 +562,8 @@ t.test('scopeAuthKey tests', t => {
526562

527563
t.same(getAuth(uri, { ...opts, spec: '@scope/foo@npm:foo@latest' }), {
528564
scopeAuthKey: null,
565+
regKey: false,
566+
authKey: null,
529567
auth: null,
530568
isBasicAuth: false,
531569
token: null,
@@ -547,6 +585,8 @@ t.test('registry host matches, path does not, send auth', t => {
547585
const uri = 'https://scope-host.com/blahblah/bloobloo/foo.tgz'
548586
t.same(getAuth(uri, { ...opts, spec: '@scope/foo' }), {
549587
scopeAuthKey: null,
588+
regKey: '//scope-host.com/scope/host/',
589+
authKey: '_authToken',
550590
token: 'c0ffee',
551591
auth: null,
552592
isBasicAuth: false,
@@ -555,6 +595,8 @@ t.test('registry host matches, path does not, send auth', t => {
555595
})
556596
t.same(getAuth(uri, { ...opts, spec: '@other-scope/foo' }), {
557597
scopeAuthKey: '//other-scope-registry.com/other/scope/',
598+
regKey: '//other-scope-registry.com/other/scope/',
599+
authKey: '_authToken',
558600
token: null,
559601
auth: null,
560602
isBasicAuth: false,
@@ -563,6 +605,8 @@ t.test('registry host matches, path does not, send auth', t => {
563605
})
564606
t.same(getAuth(uri, { ...opts, registry: 'https://scope-host.com/scope/host/' }), {
565607
scopeAuthKey: null,
608+
regKey: '//scope-host.com/scope/host/',
609+
authKey: '_authToken',
566610
token: 'c0ffee',
567611
auth: null,
568612
isBasicAuth: false,

0 commit comments

Comments
 (0)