Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

logout bugfixes #6895

Merged
merged 5 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions lib/commands/logout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const getAuth = require('npm-registry-fetch/lib/auth.js')
const npmFetch = require('npm-registry-fetch')
const { getAuth } = npmFetch
const log = require('../utils/log-shim')
const BaseCommand = require('../base-command.js')

Expand All @@ -19,10 +19,14 @@ class Logout extends BaseCommand {

const auth = getAuth(reg, this.npm.flatOptions)

const level = this.npm.config.find(`${auth.regKey}:${auth.authKey}`)

// find the config level and only delete from there
if (auth.token) {
log.verbose('logout', `clearing token for ${reg}`)
await npmFetch(`/-/user/token/${encodeURIComponent(auth.token)}`, {
...this.npm.flatOptions,
registry: reg,
method: 'DELETE',
ignoreBody: true,
})
Expand All @@ -34,12 +38,12 @@ class Logout extends BaseCommand {
}

if (scope) {
this.npm.config.delete(regRef, 'user')
this.npm.config.delete(regRef, level)
}

this.npm.config.clearCredentialsByURI(reg)
this.npm.config.clearCredentialsByURI(reg, level)

await this.npm.config.save('user')
await this.npm.config.save(level)
}
}
module.exports = Logout
5 changes: 5 additions & 0 deletions mock-registry/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,11 @@ class MockRegistry {
.reply(200, { token })
}

logout (token) {
this.nock = this.nock.delete(this.fullPath(`/-/user/token/${encodeURIComponent(token)}`))
.reply(200, { ok: true })
}

// team can be a team or a username
getPackages ({ user, team, packages = {}, times = 1, responseCode = 200 }) {
let uri
Expand Down
64 changes: 50 additions & 14 deletions node_modules/npm-registry-fetch/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,40 @@ 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)
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 +80,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 +108,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: _authKey } = regFromURI(registry, opts)
return new Auth({ scopeAuthKey, regKey: scopeAuthKey, authKey: _authKey })
}
}

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

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

class Auth {
constructor ({ token, auth, username, password, scopeAuthKey, certfile, keyfile }) {
constructor ({
token,
auth,
username,
password,
scopeAuthKey,
certfile,
keyfile,
regKey,
authKey,
}) {
// 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
2 changes: 2 additions & 0 deletions node_modules/npm-registry-fetch/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ function regFetch (uri, /* istanbul ignore next */ opts_ = {}) {
return Promise.resolve(body).then(doFetch)
}

module.exports.getAuth = getAuth

module.exports.json = fetchJSON
function fetchJSON (uri, opts) {
return regFetch(uri, opts).then(res => res.json())
Expand Down
14 changes: 4 additions & 10 deletions node_modules/npm-registry-fetch/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "npm-registry-fetch",
"version": "16.0.0",
"version": "16.1.0",
"description": "Fetch-based http client for use with npm registry APIs",
"main": "lib",
"files": [
Expand Down Expand Up @@ -41,7 +41,7 @@
},
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/template-oss": "4.18.0",
"@npmcli/template-oss": "4.19.0",
"cacache": "^18.0.0",
"nock": "^13.2.4",
"require-inject": "^1.4.4",
Expand All @@ -61,13 +61,7 @@
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.18.0",
"publish": "true",
"ciVersions": [
"16.14.0",
"16.x",
"18.0.0",
"18.x"
]
"version": "4.19.0",
"publish": "true"
}
}
8 changes: 4 additions & 4 deletions package-lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
"npm-package-arg": "^11.0.1",
"npm-pick-manifest": "^9.0.0",
"npm-profile": "^9.0.0",
"npm-registry-fetch": "^16.0.0",
"npm-registry-fetch": "^16.1.0",
"npm-user-validate": "^2.0.0",
"npmlog": "^7.0.1",
"p-map": "^4.0.0",
Expand Down Expand Up @@ -10927,9 +10927,9 @@
}
},
"node_modules/npm-registry-fetch": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.0.0.tgz",
"integrity": "sha512-JFCpAPUpvpwfSydv99u85yhP68rNIxSFmDpNbNnRWKSe3gpjHnWL8v320gATwRzjtgmZ9Jfe37+ZPOLZPwz6BQ==",
"version": "16.1.0",
"resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-16.1.0.tgz",
"integrity": "sha512-PQCELXKt8Azvxnt5Y85GseQDJJlglTFM9L9U9gkv2y4e9s0k3GVDdOx3YoB6gm2Do0hlkzC39iCGXby+Wve1Bw==",
"inBundle": true,
"dependencies": {
"make-fetch-happen": "^13.0.0",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
"npm-package-arg": "^11.0.1",
"npm-pick-manifest": "^9.0.0",
"npm-profile": "^9.0.0",
"npm-registry-fetch": "^16.0.0",
"npm-registry-fetch": "^16.1.0",
"npm-user-validate": "^2.0.0",
"npmlog": "^7.0.1",
"p-map": "^4.0.0",
Expand Down
Loading
Loading