-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Fix scoped pkg auth when using npm private pkgs via npm login --scope #5162
Conversation
This change will decrease the build size from 10.4 MB to 10.4 MB, a decrease of 1.49 KB (0%)
|
@@ -197,69 +197,31 @@ describe('request', () => { | |||
|
|||
npmRegistry.config = { | |||
'//some.other.registry/:_authToken': 'testScopedAuthToken', | |||
'@testScope:registry': '//some.other.registry/', | |||
'@testScope:registry': 'https://some.other.registry/', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By default, npm login
populates the .npmrc
file with the full url, e.g.
@scope:registry=https://some.other.registry/
and not
@scope:registry=//some.other.registry/
I made sure this is not a regression by editing away the https://
in my ~/.npmrc
and running npm install
which gives me npm ERR! Only absolute URLs are supported
__tests__/registries/npm-registry.js
Outdated
); | ||
|
||
npmRegistry.config = { | ||
'custom-host-suffix': 'pkgs.host.com', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The custom-host-suffix
feature is no longer necessary. If the request is being made to a registry that needs a token - the token will get sent. If the requested registry does no have a token, no token is sent.
Though, looking at this test a bit more now, I might have misunderstood. If someone is using a registry with a full path, e.g. http://pkgs.host.com/bar/baz
instead of just http://pkgs.host.com/
, removal of this "custom-host-suffix" feature might indeed introduce a regression.
But why not just specify the registry as http://pkgs.host.com/
? You basically trust that server.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The custom-host-suffix
feature is the only point in this PR that I'm unsure about.
This PR preserves the feature where the authorization token is only sent if it's applicable to that registry (which was one of the intentions of #3987). But I don't look at pathnames.
Fix #3987 made sure that pathname and not just host is also checked when deciding if auth header should be used. But I'm not convinced that's needed. The reasoning is briefly described here: https://github.com/yarnpkg/website/pull/504/files by @lumaxis, but I need to understand that use case better. Are you around to discuss this @lumaxis? In particular how can I setup this VSTS registry to test yarn?
I will dive deeper on this will get back here with my conclusions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @KidkArolis, thanks for the ping!
Unfortunately, I haven't really been keeping up with the custom-host-suffix
feature because for unrelated reasons, we're still using NPM in the projects I'm currently working on.
Looking at one of our internal VSTS package feeds though, it appears that they "fixed" my original issue by simply adding both URLs with the slightly different paths to the snippet they have you copy to your ~/.npmrc
:
//team-name.pkgs.visualstudio.com/_packaging/ProjectName/npm/registry/:_authToken=$TOKEN
//team-name.pkgs.visualstudio.com/_packaging/ProjectName/npm/:_authToken=$TOKEN
My work in #3231 was really only picking up an existing solution proposed in #2507. Overall, I think my specific problem with VSTS would still be solved if the custom-host-suffix
feature were removed. I remember however that other people who were using other private package feeds were looking forward to the feature too. I don't know if they actually ended up using it though.
Does this help you at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the info!
My conclusion is then that one could still use VSTS if the ~/.npmrc
looked something like:
registry=https://team-name.pkgs.visualstudio.com/
//team-name.pkgs.visualstudio.com/:_authToken=$TOKEN
or if using scopes
@foo:registry=https://team-name.pkgs.visualstudio.com/
//team-name.pkgs.visualstudio.com/:_authToken=$TOKEN
With config like that, authorization headers would be sent correctly. So it's good to know this would work for that VSTS case.
Next, I'll look into how exactly npm
handles the case when registry config contains a path, I found the relevant bit of code: https://github.com/npm/npm/blob/5e426a78ca02d0044f8dd26e0c5f881217081cbd/lib/config/fetch-opts.js#L46-L67. But haven't had a chance to look mode deeply yet.
src/registries/npm-registry.js
Outdated
// this.token must be checked to account for publish requests on non-scopped packages | ||
if (this.token || (isToRegistry && (alwaysAuth || this.isScopedPackage(packageIdent)))) { | ||
if (this.token || alwaysAuth || this.isScopedPackage(packageIdent)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The key change is basically:
We stop checking if request isToRegistry (which is a strange and slightly complicated thing to understand, of course request is being made to a registry, what else would it be made to?)
And instead we make use of this.getAuth
to figure out if this specific request needs a token or not.
In fact, we could push these 3 conditions (this.token
, alwaysAuth
, scopedPackage
) to getAuth
making this method much cleaner. getAuth
takes a url and figures out if authorization is required or not. Should I do that?
@@ -271,6 +249,7 @@ export default class NpmRegistry extends Registry { | |||
if (registry) { | |||
return String(registry); | |||
} | |||
return ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is to make sure that if URL is with a protocol, e.g. https://github.com
, we don't "assume" this is registry.npmjs.org
. This change is making getRegistry
stricter. Since this method is used only for authentication and scope resolving purposes, we basically make sure to be strict and only return registry.npmjs.org
if request is really to that registry.
src/registries/npm-registry.js
Outdated
|
||
if (!baseRegistry) { | ||
return ''; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that we more strictly check what registry the request is for (e.g. don't conclude a request to https://github.com
is to known registry), we can conclude that non registry requests don't need auth. This is basically the replacement for the isRequestToRegistry
.
availableRegistries.push(DEFAULT_REGISTRY); | ||
} | ||
return availableRegistries; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DEFAULT_REGISTRY
and YARN_REGISTRY
are both available registries even if not mentioned in any of the config. Make sure this is reflected as we now rely on this when extracting registry from a request url.
In theory only DEFAULT_REGISTRY
should suffice, but I wanted to make sure that if someone sets their default registry for some reason to be the yarn registry, things should work the same.
@KidkArolis thank a lot for a PR and your annotations! Pleas ping here if review gets stuck at any point |
I installed the build version and after that the error in #4451 (comment) was no longer reproducible. I.e. the bug seems to be solved for me. 👍 |
Currently, multiple functions are used to decide if a given request URL/pkg requires authorization. This leads to subtle issues like the on #4451. The key point of this PR is to simplify the authorization decision by making sure that The only blocker to this PR currently is figuring out whether |
c8159cd
to
c9dae10
Compare
c9dae10
to
ea3fba0
Compare
Wow, this is quite deep. Took me a couple hours to write this comment during which I realized twice some issues with this PR. But I think I've figured some things out, just need a break ;). tl;drThis PR is not yet ready to be rolled out. I realized that removing In the meantime, I've split all the changes into 4 independently reviewable commits if you fancy a look. The longer versionThing is, this An example of a registry without any path:
An example of a registry with a path (e.g. Artifactory, Nexus, VSTS):
The issue that I was gonna explain why I thought ConclusionIn conclusion, I'll revisit this PR to make sure:
Oh how I wish we could simplify this code further, it seems quite brittle and has been worked on a lot over the years. I think the main reason this might be so complicated is because request can be called in all of the following patterns: request('@scope/pkg', {})
request('@scope/pkg', {}, '@scope/pkg')
request('https://registry/@scope/pkg.tar.gz', {})
request('https://registry/@scope/pkg.tar.gz', {}, '@scope/pkg') But I think they're all needed:
Anyway, something to think about.. What about npm?Haven't looked deeply at their auth code yet. But looks like folks at npm are also dealing with similar issues. Here is an issue with recent comments about artifactory installs not working: npm/npm#16528. |
@KidkArolis thank you so much for your attention to detail on this issue 🍻 |
Amazing work @KidkArolis ! Your comment is a perfect summary of the problem and will be a tremendous help for anyone trying to understand why we added |
I've opened a new PR with a much smaller change to the code (and bigger change to the tests) - #5216. Closing this for now as it changed the code too much in a direction that wasn't fully correct. |
Fixes #4157, #4451
Summary
This attempts to squash one of the last remaining auth bugs in yarn (🤞). There currently exists an edge case where if you login to the registry using
npm login --scope=@foo
, the contents of .npmrc becomeInstalling pkgs from @foo however does not work. That's because the request URL is being made to
https://registry.yarnpkg.com
andisRequestToRegistry
returns false because the args to it areisRequestToRegistry('https://registry.yarnpkg.com/@foo/pkg', 'https://registry.npmjs.org')
.This PR attempts to remove
isRequestToRegistry
entirely, because I believe at this stage it is no longer needed. The only purpose of this function is to make sure that auth tokens for registry X are only sent with requests to registry X. This is now ensured bygetAuth
function which looks at the request url or package identifier and decides what token to use.Before this PR,
getAuth('http://Y)
would sometimes return the auth token forregistry.npmjs.org
since that's the default registry. Instead, it now returns no token since the request url does not match the registry.Let me know if I can clarify something further.
Test plan
I've tested this manually using my own npm private account. It would be great to get that e2e test going as discussed here: #4186. I could try working on that next if there's interest, but I'd do that in a separate PR.
I've also added a new unit test for this edge case
npm login --scope=@foo
. In other words, all existing tests pass (with a small exception - seeshould add authorization header with token for custom registries with a scoped package
test) + I've added a new test to cover for the edge case in question.