-
Notifications
You must be signed in to change notification settings - Fork 255
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
[DCR]: Unnecessary 401 Unauthorized api calls made during package restore #11600
Comments
@chris-smith-zocdoc - From my initial investigation regarding this request, there is a way to pass credentials on the first request itself without two requests (one request without credentials followed by second request with credentials).
The passwords are stored in encrypted form on windows & on Mono for non-Windows environment. The catch here is that encrypted passwords can only be decrypted when used on the same machine and via the same user as the original encryption. This can be a challenge for CI machines which are running under a different user context. One way to address this problem is adding package source credentials on CI using
|
Thats not the behavior I'm seeing @kartheekp-ms Please note: I'm not using Here is how my nuget.config is setup <?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="Artifactory" value="[removed]/artifactory/api/nuget/v3/nuget" protocolVersion="3"/>
</packageSources>
<packageSourceCredentials>
<Artifactory>
<add key="Username" value="[removed]" />
<add key="ClearTextPassword" value="[removed]" />
</Artifactory>
</packageSourceCredentials>
</configuration> Example logs
I verified this on OSX, but I believe this is happening on Linux also. I haven't checked windows
|
I didn't explore |
https://docs.microsoft.com/en-us/dotnet/api/system.net.httpwebrequest.preauthenticate?view=net-6.0#remarks clearly explains the behavior of |
In case someone else takes up this issue, here's my finding from the PR I started back in October: Currently NuGet sets In practise it might be that only BASIC auth is only ever used, but in theory DIGEST or NEGOTIATE might be used too, and there's no way for us to know. If we want to only support BASIC, perhaps we can add an environment variable to opt-out of the feature, so customers can report bugs to us and we'll need to redesign how we implement it. Anyway, the second problem is from the docs that Kartheek linked to above. By default, HttpClientHandler will ALWAYS first make an unauthenticated request, and then send the auth header when it gets an HTTP 401 response. The only solution to this that I could find is to set edit: I also never investigated how credential providers work, so I don't know if the Azure Artifacts cred provider require us to send a BEARER token, or if it uses BASIC. This will obviously be very important to test, even if we only want to support BASIC for username + passwords saved in nuget.config files. |
In #12451, I suggested that we can add a way to mark certain sources as auth required/recommended. |
The Negotiate auth scheme is definitely in use with on-premises Azure DevOps Server. |
In that case, my understanding of how Negotiate works means it's impossible to implement @nkolev92's idea to have a source that pre-authenticates (without being prompted by an HTTP 401) is impossible as a general case. That (sub) feature will need to be limited to HTTP Basic and cred-provider auth.
The docs on
edit: my previous testing apparently was wrong. Despite the docs sounding like the full URL up to the last slash must match a previously authenticated must match (think
NuGet.Protocol's SourceRepository type has a kind of "dependency injection" system, and I thought it caches the HttpClient and HttpClientHandler within some of the resources, but now I'm doubting myself. It's the end of my day, so I'm not going to validate right away, but an "easy" way to validate should be to set |
@jmyersmsft - Can you please take a look at NuGet/NuGet.Client#4842 (comment) comment and let us know if it possible to authenticate with I tried to authenticate with I am recommending a dummy call to the registration base (or other) resources so that future requests to those resources will send credentials when |
Sorry, I didn't read all the comments on this issue, @zivkan it looks like currently preauth is supported only for basic credential scheme. |
So it turns out my previous testing of Therefore, if a server puts all resources in the service index as subdirectories under the service index, then it will work, except if the service index is cacled. So, nuget.org's URL pattern, where it puts search and package upload on different hostnames, won't work with PreAuthenticate. nuget.org doesn't support private feeds, I'm just trying to point out that nuget.org's service index will not be a good precedent for other server implementors to copy. Similar, Azure Artifacts' current implementation where service index entries use GUIDs in their URL when the customer's configured feed uses "human friendly" names, it also wouldn't work. But the artifactory examples used earlier in this issue would work. As mentioned in the paragraph above, caching causes another problem. NuGet saves all successful responses to the filesystem, and when this filesystem is less than 30-40 minutes old, it will avoid sending an HTTP request. While this is normally good for perf, if we avoid making the HTTP request to the service index, than PreAuthenticate won't work since there won't be a prefix URL in the credential cache. An example scenario I'm thinking of is a customer tries Another example, which won't be obvious to customers, is projects using MSBuild SDKs that need to be downloaded (a very common scenario for CI builds, as CI agents are frequently cleaned between jobs). The NuGetMSBuildSdkResolver will create While we could add special logic into NuGet to basically say "make an HTTP request to the service index once, before using the cached file", if some action would happen to ONLY use files from the HTTP cache and not need to make any HTTP requests, it would be unfortunate to make a request only to the service index URL to prepopulate HttpClient's credential cache, when it wasn't needed. Especially if it triggers an interactive popup to the customer to authenticate. |
Rather than hijacking this issue and changing what the customer wrote in order to make this an epic, I created this other tracking issue instead: Therefore, this issue is now a duplicate of the epic issue. |
Unfortunately I discovered that .NET (both Core and Framework) have a bug where HttpHandler.PreAuthenticate prevents refreshing credentials, which NuGet's credential provider architecture allows (short lived OAuth2 tokens, for example) Therefore, I'm going to revert the previous fix, which is going to regress this bug, from the server's point of view, until a different solution is implemented. I'm not going to reopen this issue though, since the other epic issue is still tracking a bunch of related changes. I just wanted to post this notification in case people are watching this issue, but not the HTTP 401 epic issue. |
NuGet Product(s) Affected
dotnet.exe
Current Behavior
We host an authenticated nuget feed for our organization using Artifactory. We noticed in the logs an excessive amount of 401 Unauthorized responses originating from
dotnet restore
throughout the org. It appears that every api call is being made twice, first without authentication, then again with authentication. This doubles the number of api calls make during a package restore.Desired Behavior
I'd like to force nuget to always send credentials to our feed, there is no need to make the api calls twice. This slows down the restore process, places additional load on the supporting infrastructure and makes monitoring more difficult.
Additional Context
Other package management tools will always send credentials if configured to do so, they do not 401 first.
The text was updated successfully, but these errors were encountered: