Skip to content

Commit

Permalink
feat(publish): Attempt profile retrieval before whoami endpoint durin…
Browse files Browse the repository at this point in the history
…g user validation
  • Loading branch information
evocateur committed Dec 3, 2018
1 parent 16611be commit 38097d8
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 5 deletions.
22 changes: 18 additions & 4 deletions commands/publish/__tests__/get-npm-username.test.js
Expand Up @@ -19,13 +19,20 @@ describe("getNpmUsername", () => {
console.error = origConsoleError;
});

test("fetches whoami endpoint", async () => {
test("fetches whoami endpoint after profile 404", async () => {
fetch.json.mockImplementationOnce(() => {
const err = new Error("third-party profile fail");

err.code = "E404";

return Promise.reject(err);
});
const opts = { such: "npm-conf", wow: true };

const username = await getNpmUsername(opts);

expect(username).toBe("lerna-test");
expect(fetch.json).toHaveBeenLastCalledWith("-/whoami", opts);
expect(fetch.json).toHaveBeenLastCalledWith("/-/whoami", opts);
});

test("throws an error when successful fetch yields empty username", async () => {
Expand All @@ -44,12 +51,19 @@ describe("getNpmUsername", () => {

test("logs failure message before throwing validation error", async () => {
fetch.json.mockImplementationOnce(() => {
const err = new Error("whoops");
const err = new Error("legacy npm Enterprise profile fail");

err.code = "E500";

return Promise.reject(err);
});
fetch.json.mockImplementationOnce(() => {
const err = new Error("third-party whoami fail");

err.code = "E404";

return Promise.reject(err);
});

const opts = new Map();
opts.set("registry", "https://registry.npmjs.org/");
Expand All @@ -59,7 +73,7 @@ describe("getNpmUsername", () => {
} catch (err) {
expect(err.prefix).toBe("EWHOAMI");
expect(err.message).toBe("Authentication error. Use `npm whoami` to troubleshoot.");
expect(console.error).toHaveBeenCalledWith("whoops");
expect(console.error).toHaveBeenCalledWith("third-party whoami fail");
}

expect.assertions(3);
Expand Down
40 changes: 39 additions & 1 deletion commands/publish/lib/get-npm-username.js
Expand Up @@ -9,7 +9,7 @@ module.exports = getNpmUsername;
function getNpmUsername(opts) {
log.info("", "Verifying npm credentials");

return fetch.json("-/whoami", opts).then(success, failure);
return getProfileData(opts).then(success, failure);

function success(result) {
log.silly("npm whoami", "received %j", result);
Expand Down Expand Up @@ -41,3 +41,41 @@ function getNpmUsername(opts) {
);
}
}

function getProfileData(opts) {
log.verbose("", "Retrieving npm user profile");

return fetch
.json("/-/npm/v1/user", opts)
.then(data => {
log.silly("npm profile get", "received %j", data);

const result = {
// remap to match legacy whoami format
username: data.name,
};

return result;
})
.catch(err => {
// Many third-party registries do not implement the user endpoint
// Legacy npm Enterprise returns E500 instead of E404
if (err.code === "E500" || err.code === "E404") {
return getWhoAmI(opts);
}

// re-throw 401 Unauthorized (and all other unexpected errors)
throw err;
});
}

function getWhoAmI(opts) {
log.verbose("", "Retrieving npm username");

return fetch.json("/-/whoami", opts).then(data => {
log.silly("npm whoami", "received %j", data);

// { username: String }
return data;
});
}

0 comments on commit 38097d8

Please sign in to comment.