Actually, my implementation evolved past this.
There are two other issues - I couldn't properly use the Chain of Command pattern with ICredentialProvider, and the VS web proxy is called incorrectly, meaning you always get prompted, and never use cached credentials.
The Chain of Command issue is that the ICredentialProvider is a singleton, so can't maintain state, and only has a boolean "retrying" parameter to work with. If you want to have multiple means of getting at credentials, that boolean really starts to hurt. For example, I use NuGet's SettingsCredentialProvider to allow credentials to be stored in package source settings, then a custom provider to look at ReSharper's own proxy settings, and then fall back to the VS web proxy service. But if the SettingsCredentialProvider tries and fails, the inner provider(s) get called with the "retrying" parameter set to true, and they have no way of knowing if they've had an attempt and failed, or if the "parent" provider has failed.
Fortunately, I'm mostly ok with this - SettingsCredentialProvider only retries if a feed url fails, and my ReSharper provider only fails if a proxy url fails, so they're mutually exclusive. However, it does mean that the VS web proxy service can be called with "retrying" set to true, and so only gets one chance instead of two.
This could be fixed by not having the providers as singletons, or by replacing the "retrying" boolean with a retry count. If the count is greater than zero, you know it's your number of retries, i.e. if you get a retry count of 0, try your first method. If you get 1, try your second, 2, third, and so on. If you run out of methods, subtract the numbr of methods and pass to the inner provider. If the inner provider hasn't tried anything, the count will be zero, otherwise it'll be the number of retries for that provider (hope this makes sense). Either of these would be breaking interface changes - HttpClient.DefaultCredentialProvider would have to be a factory (ideally, it would also be an instance property, so multiple hosts can have proxy providers - e.g. VS and ReSharper) or ICredentialProvider would have a count instead of a bool.
The second issue is that the IVsWebProxy isn't being called correctly, and the chain of command issue exacerbates this.
The idea is to repeatedly call IVsWebProxy.PrepareWebProxy while making web requests. Each time, you pass in the state of the previous call. You start with a state of NoCredentials and VS will set up the proxy with default credentials (proxy.Credentials = CredentialCache.DefaultCredentials), and return a state of DefaultCredentials. If this fails, you call again, passing in that returned DefaultCredentials state. VS then looks for cached credentials, using P/Invoke to talk to CredRead, etc. If it finds some, it applies them to the proxy and returns CachedCredentials. If it doesn't find any cached credentials, or if they fail and you call again, it will prompt the user for credentials (using CredPromptUI), and either return a state of PromptForCredentials or Abort. If you call it with PromptForCrednetials again, it will repeatedly prompt the user.
(For future reference, IVsWebProxy is implemented by VsWebProxyService and VsWebProxy in Microsoft.VisualStudio.CommonIDE.dll from the GAC)
The NuGet code calls it once with PromptForCredentials as the initial state, so it will always prompt the user, and never look for cached credentials. Also, it falls foul of the "retrying" parameter. The method needs to be called repeatedly, but with the boolean parameter, you can only call it twice - and if SettingsCredentialProvider has failed, you can only call it once. Assuming you do get to call it twice, you can call it initially with DefaultCredentials to get cached credentials, and subsequently PromptForCredentials (but you'll only have one chance for prompting). Default credentials are added by default by NuGet's RequestHelper.
I worked around this by implementing my VS web proxy ICredentialsProvider with a weak reference dictionary - I map the Uri to the current state, start with NoCredentials and allow it to retry until it gets to Abort. Having ICredentialsProvider not be a singleton would be great here. I could maintain state for the current web request and handle things a whole lot cleaner.
I'd like to see NuGet handle this cleaner - and use the VS web proxy service correctly. I'd vote for changing providers from being singleton to being per-request. I'm happy to work on a PR for this, but would like to get an agreement on approach before I work on anything.
There are two other issues - I couldn't properly use the Chain of Command pattern with ICredentialProvider, and the VS web proxy is called incorrectly, meaning you always get prompted, and never use cached credentials.
The Chain of Command issue is that the ICredentialProvider is a singleton, so can't maintain state, and only has a boolean "retrying" parameter to work with. If you want to have multiple means of getting at credentials, that boolean really starts to hurt. For example, I use NuGet's SettingsCredentialProvider to allow credentials to be stored in package source settings, then a custom provider to look at ReSharper's own proxy settings, and then fall back to the VS web proxy service. But if the SettingsCredentialProvider tries and fails, the inner provider(s) get called with the "retrying" parameter set to true, and they have no way of knowing if they've had an attempt and failed, or if the "parent" provider has failed.
Fortunately, I'm mostly ok with this - SettingsCredentialProvider only retries if a feed url fails, and my ReSharper provider only fails if a proxy url fails, so they're mutually exclusive. However, it does mean that the VS web proxy service can be called with "retrying" set to true, and so only gets one chance instead of two.
This could be fixed by not having the providers as singletons, or by replacing the "retrying" boolean with a retry count. If the count is greater than zero, you know it's your number of retries, i.e. if you get a retry count of 0, try your first method. If you get 1, try your second, 2, third, and so on. If you run out of methods, subtract the numbr of methods and pass to the inner provider. If the inner provider hasn't tried anything, the count will be zero, otherwise it'll be the number of retries for that provider (hope this makes sense). Either of these would be breaking interface changes - HttpClient.DefaultCredentialProvider would have to be a factory (ideally, it would also be an instance property, so multiple hosts can have proxy providers - e.g. VS and ReSharper) or ICredentialProvider would have a count instead of a bool.
The second issue is that the IVsWebProxy isn't being called correctly, and the chain of command issue exacerbates this.
The idea is to repeatedly call IVsWebProxy.PrepareWebProxy while making web requests. Each time, you pass in the state of the previous call. You start with a state of NoCredentials and VS will set up the proxy with default credentials (proxy.Credentials = CredentialCache.DefaultCredentials), and return a state of DefaultCredentials. If this fails, you call again, passing in that returned DefaultCredentials state. VS then looks for cached credentials, using P/Invoke to talk to CredRead, etc. If it finds some, it applies them to the proxy and returns CachedCredentials. If it doesn't find any cached credentials, or if they fail and you call again, it will prompt the user for credentials (using CredPromptUI), and either return a state of PromptForCredentials or Abort. If you call it with PromptForCrednetials again, it will repeatedly prompt the user.
(For future reference, IVsWebProxy is implemented by VsWebProxyService and VsWebProxy in Microsoft.VisualStudio.CommonIDE.dll from the GAC)
The NuGet code calls it once with PromptForCredentials as the initial state, so it will always prompt the user, and never look for cached credentials. Also, it falls foul of the "retrying" parameter. The method needs to be called repeatedly, but with the boolean parameter, you can only call it twice - and if SettingsCredentialProvider has failed, you can only call it once. Assuming you do get to call it twice, you can call it initially with DefaultCredentials to get cached credentials, and subsequently PromptForCredentials (but you'll only have one chance for prompting). Default credentials are added by default by NuGet's RequestHelper.
I worked around this by implementing my VS web proxy ICredentialsProvider with a weak reference dictionary - I map the Uri to the current state, start with NoCredentials and allow it to retry until it gets to Abort. Having ICredentialsProvider not be a singleton would be great here. I could maintain state for the current web request and handle things a whole lot cleaner.
I'd like to see NuGet handle this cleaner - and use the VS web proxy service correctly. I'd vote for changing providers from being singleton to being per-request. I'm happy to work on a PR for this, but would like to get an agreement on approach before I work on anything.