Skip to content

Commit

Permalink
feat: include regKey and authKey in auth object
Browse files Browse the repository at this point in the history
This will allow consumers to know *what* auth value provided the auth
  • Loading branch information
wraithgar committed Oct 10, 2023
1 parent 15910a0 commit a65fd90
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 14 deletions.
51 changes: 37 additions & 14 deletions lib/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ const npa = require('npm-package-arg')
const { URL } = require('url')

// Find the longest registry key that is used for some kind of auth
// in the options.
const regKeyFromURI = (uri, opts) => {
// in the options. Returns the registry key and the auth config.
const regFromURI = (uri, opts) => {
const parsed = new URL(uri)
// try to find a config key indicating we have auth for this registry
// can be one of :_authToken, :_auth, :_password and :username, or
Expand All @@ -14,23 +14,37 @@ const regKeyFromURI = (uri, opts) => {
// stopping when we reach '//'.
let regKey = `//${parsed.host}${parsed.pathname}`
while (regKey.length > '//'.length) {
const authKey = hasAuth(regKey, opts)
// got some auth for this URI
if (hasAuth(regKey, opts)) {
return regKey
if (authKey) {
return { regKey, authKey }
}

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

const hasAuth = (regKey, opts) => (
opts[`${regKey}:_authToken`] ||
opts[`${regKey}:_auth`] ||
opts[`${regKey}:username`] && opts[`${regKey}:_password`] ||
opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]
)
// Not only do we want to know if there is auth, but if we are calling `npm logout` we want to know what config value specifically provided it. This is so we can look up where the config came from to delete it (i.e. user vs project)

Check failure on line 30 in lib/auth.js

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 233. Maximum allowed is 100
const hasAuth = (regKey, opts) => {
if (opts[`${regKey}:_authToken`]) {
return '_authToken'
}
if (opts[`${regKey}:_auth`]) {
return '_auth'
}
if (opts[`${regKey}:username`] && opts[`${regKey}:_password`]) {
// 'password' can be inferred to also be present
return 'username'
}
if (opts[`${regKey}:certfile`] && opts[`${regKey}:keyfile`]) {
// 'keyfile' can be inferred to also be present
return 'certfile'
}
return false
}

const sameHost = (a, b) => {
const parsedA = new URL(a)
Expand Down Expand Up @@ -63,11 +77,14 @@ const getAuth = (uri, opts = {}) => {
if (!uri) {
throw new Error('URI is required')
}
const regKey = regKeyFromURI(uri, forceAuth || opts)
const { regKey, authKey } = regFromURI(uri, forceAuth || opts)

// we are only allowed to use what's in forceAuth if specified
if (forceAuth && !regKey) {
return new Auth({
// if we force auth we don't want to refer back to anything in config
regKey: false,
authKey: null,
scopeAuthKey: null,
token: forceAuth._authToken || forceAuth.token,
username: forceAuth.username,
Expand All @@ -88,8 +105,8 @@ const getAuth = (uri, opts = {}) => {
// registry where we logged in, but the same auth SHOULD be sent
// to that artifact host, then we track where it was coming in from,
// and warn the user if we get a 4xx error on it.
const scopeAuthKey = regKeyFromURI(registry, opts)
return new Auth({ scopeAuthKey })
const { regKey: scopeAuthKey, authKey } = regFromURI(registry, opts)

Check failure on line 108 in lib/auth.js

View workflow job for this annotation

GitHub Actions / Lint

'authKey' is already declared in the upper scope on line 80 column 19
return new Auth({ scopeAuthKey, regKey: scopeAuthKey, authKey })
}
}

Expand All @@ -104,6 +121,8 @@ const getAuth = (uri, opts = {}) => {

return new Auth({
scopeAuthKey: null,
regKey,
authKey,
token,
auth,
username,
Expand All @@ -114,8 +133,12 @@ const getAuth = (uri, opts = {}) => {
}

class Auth {
constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) {
constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile, regKey, authKey }) {

Check failure on line 136 in lib/auth.js

View workflow job for this annotation

GitHub Actions / Lint

This line has a length of 103. Maximum allowed is 100
// same as regKey but only present for scoped auth. Should have been named scopeRegKey
this.scopeAuthKey = scopeAuthKey
// `${regKey}:${authKey}` will get you back to the auth config that gave us auth
this.regKey = regKey
this.authKey = authKey
this.token = null
this.auth = null
this.isBasicAuth = false
Expand Down
44 changes: 44 additions & 0 deletions test/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ t.test('basic auth', t => {
const gotAuth = getAuth(config.registry, config)
t.same(gotAuth, {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: 'username',
token: null,
isBasicAuth: true,
auth: Buffer.from('user:pass').toString('base64'),
Expand Down Expand Up @@ -61,6 +63,8 @@ t.test('token auth', t => {
}
t.same(getAuth(`${config.registry}/foo/-/foo.tgz`, config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_authToken',
isBasicAuth: false,
token: 'c0ffee',
auth: null,
Expand Down Expand Up @@ -107,6 +111,8 @@ t.test('forceAuth', t => {
}
t.same(getAuth(config.registry, config), {
scopeAuthKey: null,
regKey: false,
authKey: null,
token: null,
isBasicAuth: true,
auth: Buffer.from('user:pass').toString('base64'),
Expand Down Expand Up @@ -140,6 +146,8 @@ t.test('forceAuth token', t => {
}
t.same(getAuth(config.registry, config), {
scopeAuthKey: null,
regKey: false,
authKey: null,
isBasicAuth: false,
token: 'cafebad',
auth: null,
Expand Down Expand Up @@ -168,6 +176,8 @@ t.test('_auth auth', t => {
}
t.same(getAuth(`${config.registry}/asdf/foo/bar/baz`, config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_auth',
token: null,
isBasicAuth: false,
auth: 'c0ffee',
Expand Down Expand Up @@ -195,6 +205,8 @@ t.test('_auth username:pass auth', t => {
}
t.same(getAuth(config.registry, config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_auth',
token: null,
isBasicAuth: false,
auth: auth,
Expand Down Expand Up @@ -246,6 +258,8 @@ t.test('globally-configured auth', t => {
}
t.same(getAuth(basicConfig.registry, basicConfig), {
scopeAuthKey: null,
regKey: '//different.registry/',
authKey: 'username',
token: null,
isBasicAuth: true,
auth: Buffer.from('globaluser:globalpass').toString('base64'),
Expand All @@ -261,6 +275,8 @@ t.test('globally-configured auth', t => {
}
t.same(getAuth(tokenConfig.registry, tokenConfig), {
scopeAuthKey: null,
regKey: '//different.registry/',
authKey: '_authToken',
token: 'deadbeef',
isBasicAuth: false,
auth: null,
Expand All @@ -276,6 +292,8 @@ t.test('globally-configured auth', t => {
}
t.same(getAuth(`${_authConfig.registry}/foo`, _authConfig), {
scopeAuthKey: null,
regKey: '//different.registry',
authKey: '_auth',
token: null,
isBasicAuth: false,
auth: 'deadbeef',
Expand All @@ -296,6 +314,8 @@ t.test('otp token passed through', t => {
}
t.same(getAuth(config.registry, config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_authToken',
token: 'c0ffee',
isBasicAuth: false,
auth: null,
Expand Down Expand Up @@ -365,6 +385,8 @@ t.test('always-auth', t => {
}
t.same(getAuth(config.registry, config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_authToken',
token: 'c0ffee',
isBasicAuth: false,
auth: null,
Expand Down Expand Up @@ -399,6 +421,8 @@ t.test('scope-based auth', t => {
}
t.same(getAuth(config['@myscope:registry'], config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_authToken',
auth: null,
isBasicAuth: false,
token: 'c0ffee',
Expand All @@ -407,6 +431,8 @@ t.test('scope-based auth', t => {
}, 'correct auth token picked out')
t.same(getAuth(config['@myscope:registry'], config), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: '_authToken',
auth: null,
isBasicAuth: false,
token: 'c0ffee',
Expand Down Expand Up @@ -446,6 +472,8 @@ t.test('certfile and keyfile errors', t => {
'//my.custom.registry/here/:keyfile': `${dir}/nosuch.key`,
}), {
scopeAuthKey: null,
regKey: '//my.custom.registry/here/',
authKey: 'certfile',
auth: null,
isBasicAuth: false,
token: null,
Expand Down Expand Up @@ -479,6 +507,8 @@ t.test('do not be thrown by other weird configs', t => {
const auth = getAuth(uri, opts)
t.same(auth, {
scopeAuthKey: null,
regKey: '//localhost:15443/foo',
authKey: '_authToken',
token: 'correct bearer token',
isBasicAuth: false,
auth: null,
Expand All @@ -499,6 +529,8 @@ t.test('scopeAuthKey tests', t => {

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

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

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

t.same(getAuth(uri, { ...opts, spec: '@scope/foo@npm:foo@latest' }), {
scopeAuthKey: null,
regKey: false,
authKey: null,
auth: null,
isBasicAuth: false,
token: null,
Expand All @@ -547,6 +585,8 @@ t.test('registry host matches, path does not, send auth', t => {
const uri = 'https://scope-host.com/blahblah/bloobloo/foo.tgz'
t.same(getAuth(uri, { ...opts, spec: '@scope/foo' }), {
scopeAuthKey: null,
regKey: '//scope-host.com/scope/host/',
authKey: '_authToken',
token: 'c0ffee',
auth: null,
isBasicAuth: false,
Expand All @@ -555,6 +595,8 @@ t.test('registry host matches, path does not, send auth', t => {
})
t.same(getAuth(uri, { ...opts, spec: '@other-scope/foo' }), {
scopeAuthKey: '//other-scope-registry.com/other/scope/',
regKey: '//other-scope-registry.com/other/scope/',
authKey: '_authToken',
token: null,
auth: null,
isBasicAuth: false,
Expand All @@ -563,6 +605,8 @@ t.test('registry host matches, path does not, send auth', t => {
})
t.same(getAuth(uri, { ...opts, registry: 'https://scope-host.com/scope/host/' }), {
scopeAuthKey: null,
regKey: '//scope-host.com/scope/host/',
authKey: '_authToken',
token: 'c0ffee',
auth: null,
isBasicAuth: false,
Expand Down

0 comments on commit a65fd90

Please sign in to comment.