Authentication for repositories

Dec 11, 2010 at 11:28 AM

I would like to discuss how we can achieve the ability to authenticate against a repository. In the first step standard authentication mechanisms (basic http+ssl/ntlm) that protect the feed and downloads would be the goal. Individual authorization on specific downloads would be the second step.

I’ve already forked nugget to support basic +ssl by placing the username and password into the url:

http://user:password@www.myfeedserver.com/feed

What I don’t like is that the password is stored in clear text…

The easiest way would be to store feed urls in encrypted format!

A more mature solution would be to provide an explicit place to enter and also to store credentials. But how to protect them on a per user basis - the only idea I currently have is a user certificate.

Daniel

 

Dec 11, 2010 at 1:50 PM

Why not use the windows credentials manager and let the os do its work?

from android nexus1

On Dec 11, 2010 9:29 AM, "lennybacon" <notifications@codeplex.com> wrote:
> From: lennybacon
>
> I would like to discuss how we can achieve the ability to authenticate against a repository. In the first step standard authentication mechanisms (basic http+ssl/ntlm) that protect the feed and downloads would be the goal. Individual authorization on specific downloads would be the second step.I've already forked nugget to support basic +ssl by placing the username and password into the url:http://user:password@www.myfeedserver.com/feedWhat I don't like is that the password is stored in clear text…The easiest way would be to store feed urls in encrypted format!A more mature solution would be to provide an explicit place to enter and also to store credentials. But how to protect them on a per user basis - the only idea I currently have is a user certificate.Daniel
>
>
Dec 11, 2010 at 2:45 PM

+1

sent from my mobile

On Dec 11, 2010 8:50 AM, "dcazzulino" <notifications@codeplex.com> wrote:
> From: dcazzulino
>
> Why not use the windows credentials manager and let the os do its work?from android nexus1On Dec 11, 2010 9:29 AM, "lennybacon" <notifications@codeplex.com> wrote:> From: lennybacon
>>
>> I would like to discuss how we can achieve the ability to authenticate against a repository. In the first step standard authentication mechanisms (basic http+ssl/ntlm) that protect the feed and downloads would be the goal. Individual authorization on specific downloads would be the second step.I've already forked nugget to support basic +ssl by placing the username and password into the url:http://user:password@www.myfeedserver.com/feedWhat I don't like is that the password is stored in clear text…The easiest way would be to store feed urls in encrypted format!A more mature solution would be to provide an explicit place to enter and also to store credentials. But how to protect them on a per user basis - the only idea I currently have is a user certificate.Daniel
>>
>>
>
>
>
Developer
Dec 11, 2010 at 4:52 PM

What scenario are you trying to accomplish?

Dec 11, 2010 at 7:22 PM

Internet reachable private repositories? Just like a private cloud hosted tfs...

from android nexus1

On Dec 11, 2010 2:52 PM, "dfowler" <notifications@codeplex.com> wrote:
> From: dfowler
>
> What scenario are you trying to accomplish?
>
>
Dec 11, 2010 at 7:28 PM

>> Why not use the windows credentials manager and let the os do its work?

@dcazzulino: This is not possible due to the security restrictions of the hosted Powershell. If tried several ways to accomplish that.

>> Internet reachable private repositories? Just like a private cloud hosted tfs...

That is what I want.

 

Dec 12, 2010 at 3:14 PM

Can you describe what the security restrictions of host PS is? What did you try and what problem you encountered?

Jun 1, 2011 at 11:58 AM

I am reusing this discussion thread for Work Item #366

@lennybacon: Adding the credentials to the Url of the request is probably not the best option as we already have other ways of gathering credentials from the user depending on which client is being used. For example if we are using a console client such as NuGet.exe then we first attempt integrated authentication and if that is not valid then we prompt the user in the console. For Visual Studio VSIX package which is both the Manage Packages Dialog and the Package Manager Console I am currently finalizing testing of getting credentials through the already provided proxy service that Visual Studio shell exposes to us which is the same logic that the Visual Studio Extension Manager uses to get credentials when you browse the online extensions gallery from the www.VisualStudioGallery.com website. This proxy service is actually using the Windows Credential Manager that you've mentioned and I have implemented the logic so that it would first see if we already have saved credentials first and test them to make sure that they are valid before prompting the user so that we can minimize the credential prompting.

Jun 1, 2011 at 12:32 PM

@ilyalozovyy looking forward to check your impl. :-)

Jun 1, 2011 at 1:11 PM

Ok, continuing discussion of issue 366 http://nuget.codeplex.com/workitem/366 here.

I had actually looked at @lennybacon's proposed solution. However, as he pointed out himself, having the password stored in plain text in the repository URL could be considered a security issue.

It's also been mentioned that using the Windows Credential Store may be an issue due to security restrinctions of the hosted Powershell.

My original thoughts were to simply save the credentials as an encypted value in NuGet.config similar to how the API Key is stored. Since this uses the ProtectedData class in the Current User scope, I'm pretty confident this is secure enough for our needs.

I would definitely like to leverage any work already done by @ilyalozovyy with respect to acquiring user credentials.

Ok, now I have a couple of design questions.

I ran into an issue when building the CredentialCache.  The Bootstrapper project currrently "links" to HttpClient. This is used to download an update to the nuget command line executable.

However, this class is also where I assign the CredentialCache in the InitializeRequest method. Since I want to build the CredentialCache from user settings, I was getting a compile error on Bootstrapper since Settings was not defined in that context. I tried linking in the Settings classes, but then I would have had to bring in a whole boat-load of classes like IFileSystem PhysicalFileSystem, XmlUtility, etc. Ugh!

So I decided to refactor and create an ICredentialCacheProvider interface. The problem that I'm having is how to inject it into HttpClient. We're not using dependency injection, and there are several places where we new HttpClient. I'm thinking of doing something similar to the Proxy support with a RegisterProvider-like method.

My main concern now is that any client that wants to use the CredentialCache will have to first register the proper provider before creating an instance of HttpClient. I see that DataServicePackageRepository has a quasi-HttpClientFactory, where I can register the provider. The other place that uses HttpClient directly is PackageDownloader.

As we add more functionality, I think we're starting to see some pain points in the current codebase. Since this is the Core, I don't want to make any fundamental changes without discussing how to proceed.

Any thoughts on how you would like to go?

Thanks!

 

 

Jun 1, 2011 at 1:17 PM

@lennybacon: I want to repeat my question above. What is the security restriction of the nuget powershell host? Can you elaborate?

Jun 1, 2011 at 1:53 PM
Edited Jun 1, 2011 at 1:53 PM

@dotnetjunky As I tried to call the Unmanaged Windows Credential Manager exceptions were thrown telling me it is not possible to call unmanaged code. So its a CAS issue. see https://hg01.codeplex.com/forks/lennybacon/lennybaconnugetcontrib

I guess the code @ilyalozovyy is contributing will work around this by having trusted code (e.g. GAC) that calls the unmanaged Windows Credential Manager API.

Jun 1, 2011 at 2:00 PM
Edited Jun 1, 2011 at 2:07 PM

Oisin recently added code in the powershell host to support opening the credential manager dialog. You can look into his implementaiton to get an idea.

Jun 1, 2011 at 4:34 PM

Here are some screenshots for the authentication feature. Let me know what you think. I'm not sure I like how the bottom section is starting to look really busy. Anyway, this is my initial stab at it.

Enter package source name and URL, then click Connect as...

Enter username and password and click OK

Package source is added and a lock icon is displayed to indicate it is authenticated

Coordinator
Jun 1, 2011 at 5:49 PM

I wonder if we can avoid having the Connect As.. button at all. Putting my security hat on, it might not be a good idea to store passwords indefinitely in the config. For example, it’s not too hard for a domain name to get hijacked (http://secretgeek.net/sg_hijack_1.asp).

Instead, what if the workflow was this:

1. No changes to the Package Sources dialog.

2. When a 401 error occurs, we show the the credentials dialog.Description: http://www.systemex.net/nuget/images/CredentialUI.jpg

3. When the user enters their information, we store it with an expiration date (30 days?).

4. The next time they make a request for that repository, we supply the credentials (for the next 30 days).

That way, there’s no need to clutter the Options dialog with something that 99% of people won’t need.

Thoughts?

Jun 1, 2011 at 6:27 PM

I just talked to @ilyalozovyy on Skype. Looks like we're now all in agreement that the credentials will be prompted on-demand the first time a repository is accessed tht requires authentication.

So the UI changes will not be needed, although I would like to clean it up.

I'm going to let @ilyalozovyy take the lead on this feature, but will give him assistance as needed.

Coordinator
Jun 1, 2011 at 8:55 PM

Excellent!

Speaking of the package sources dialog, we did have some discussions about improving the UI for it. If you have ideas, let’s start a new discussion. I’ll have to find my notes about it. J

Developer
Jun 28, 2011 at 9:50 AM

If you need to test this: MyGet now has private feed support with basic auth, only thing you have to do is set the private feed access control to "no access"

Jun 28, 2011 at 11:20 AM

@maartenba: Thanks for the heads up. I've been slacking for a few days but I have finally setup a proxy server at home so that I don't have to use my work environment to test all of the different scenarios and now I'll have to setup an account with myget so that I can do a complete test of making requests through a proxy and then to authenticate against private repos.

Coordinator
Jun 28, 2011 at 3:59 PM

Is this something that could be ported easily to NuGet.Server?

Developer
Jun 28, 2011 at 6:47 PM
Edited Jun 28, 2011 at 6:50 PM

@Haacked yes it is. Can I just fork, add and push? If ou prefer to add it yourself, here's the fastest way to do it: http://blog.maartenballiauw.be/post/2011/06/28/Enabling-conditional-Basic-HTTP-authentication-on-a-WCF-OData-service.aspx

[edit]Adding basic / NTLM auth to the IIS server hosting your NuGet.Server will do the same. I'm not sure if this should be implemented in NuGet.Server or just in IIS?[/edit]

Coordinator
Jun 28, 2011 at 8:11 PM

I’d prefer it if you forked, added, and pushed and then issue a pull request. That way you get the credit for the fix in our hg history as you deserve. J

Developer
Jun 29, 2011 at 10:20 AM

There you go :-) http://nuget.codeplex.com/SourceControl/network/Forks/maartenba/nugetserverauth/contribution/1304

Jun 29, 2011 at 10:34 AM

So what about the client bits for auth?

@ilyalozovyy: What is the state? Do you need help?

--Daniel

Jun 29, 2011 at 10:08 PM

@lennybacon: working...... the changes are not that big but just having some hard time coming up with a good way to do it consistently for both the proxy authentication and request authentication. Once I get that figured out then we should be ready to test.

Jun 30, 2011 at 2:22 AM
dotnetjunky wrote:

Oisin recently added code in the powershell host to support opening the credential manager dialog. You can look into his implementaiton to get an idea.


Yeah, you can use Get-Credential now in the powershell host in NuGet to get the Windows standard credential dialog, or you can even get down and dirty with $host.ui.ReadLineAsSecureString(), but Get-Credential is better as you can prepopulate with a username.

Jul 6, 2011 at 12:59 PM

A quick update in case anyone still cares about this issue.

  1. The Proxy and Request based authentication support has been implemented and is currently being tested by a few members of the active NuGet community. One issue did come up which will require a little bit more investigation that possibly deals with how we handle SSL requests.
  2. I have also communicated with @maartenba in regards to the Authentication support for NuGet.Server and I believe that we are in agreement that the security of the feeds really should be a configuration of the IIS server or the web server in general and we should not be doing custom coding in order to request credentials from the user. @maartenba please correct me if I am wrong about this.

There are a couple of small code changes that I need to make based on recommendation from @lennybacon and I will submit the code changes for a code review.

If anyone has any questions about this please let me know and I will try to address them as soon as possible.

Jul 6, 2011 at 1:17 PM

@ilyalozovyy I have used SSL and IIS Basic Authentication successfully. I agree that we shouldn't code security code if we can rely on the platform (IIS).  

Jul 6, 2011 at 1:27 PM
Great work guys. I can't wait to see it in nuget and package explorer.

I assume you have also tested with integrated auth correct?
>
Jul 6, 2011 at 1:33 PM

@dotnetjunky: yep integrated and windows authentication support is in place. The most interesting part of this implementation which is also kind of hackish was the part where I am trying to re-use the VsWebProxy service to perform both Proxy and Request authentication since the VsWebProxy class us always updating the WebRequest.DefaultWebProxy static property and I have to maintain it's original state and re-set it after the prompt. So I would like to get it into as many hands as possible for testing once I've updated the code to ensure that we don't run into any issues.

Developer
Jul 6, 2011 at 1:44 PM

I agree completely. IIS can be used to implement this functionality.

Jul 6, 2011 at 2:56 PM

@lennybacon, which fork are you using? Something seems to be wrong with @ilyalozovyy's fork (https://hg01.codeplex.com/forks/ilyalozovyy/repoauth)

I get the following exception using just the official NuGet feed. It seems like ProxyFinder.GetSystemProxy() is not returning the correct default proxy if no proxy is configured. It simply creates a new WebProxy with the Uri that is passed in (which happens to be https:) and this causes the error.

Perhaps I'm looking at a very old fork?

The URI is https://go.microsoft.com/fwlink/?LinkID=206669

 System.NotSupportedException was unhandled by user code
  Message=The ServicePointManager does not support proxies with the https scheme.
  Source=System
  StackTrace:
       at System.Net.ServicePointManager.FindServicePointHelper(Uri address, Boolean isProxyServicePoint)
       at System.Net.ServicePointManager.FindServicePoint(Uri address, IWebProxy proxy, ProxyChain& chain, HttpAbortDelegate& abortDelegate, Int32& abortState)
       at System.Net.HttpWebRequest.FindServicePoint(Boolean forceFind)
       at System.Net.HttpWebRequest.set_InternalProxy(IWebProxy value)
       at System.Net.HttpWebRequest.set_Proxy(IWebProxy value)
       at NuGet.ProxyFinder.IsProxyValid(IWebProxy proxy, Uri uri) in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\ProxyFinder.cs:line 148
       at NuGet.ProxyFinder.GetProxyInternal(Uri uri) in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\ProxyFinder.cs:line 102
       at NuGet.ProxyFinder.GetProxy(Uri uri) in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\ProxyFinder.cs:line 73
       at NuGet.HttpClient.InitializeRequest(WebRequest request) in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\HttpClient.cs:line 99
       at NuGet.HttpClient.CreateRequest() in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\HttpClient.cs:line 80
       at NuGet.RedirectedHttpClient.EnsureClient() in C:\Users\Michael\Projects\nuget-auth\src\Core\Utility\RedirectedHttpClient.cs:line 53
       at System.Lazy`1.CreateValue()
  InnerException:

Jul 6, 2011 at 3:41 PM

I haven't tried querying the official feed with the fork :-(

Coordinator
Jul 6, 2011 at 3:45 PM

Regarding this:

I have also communicated with @maartenba in regards to the Authentication support for NuGet.Server and I believe that we are in agreement that the security of the feeds really should be a configuration of the IIS server or the web server in general

That might work fine for now. But at some point we may want to add simple membership to NuGet.Server. At that point, having basic auth for the OData endpoint using users in our database won’t be possible without some code.

But for now, I think doing less is the right approach J

Jul 6, 2011 at 4:35 PM

@kiliman: can you describe your internet settings to me because what it looks like the ProxyFinder thinks that you do have a valid proxy defined in the system configuration and it allows the proxy finder to continue the credential discovery process however because the proxy configuration is not really valid it fails due to the unsupported HTTPS proxy. If you can please take a look at the following method: ProxyFinder.IsSystemProxySet(Uri) and let me know what you are seeing there and what it thinks your WebRequest.DefaultWebProxy is currently set to.

Jul 7, 2011 at 1:45 PM

@ilyaloxovyy I stepped through the code and DefaultWebProxy is not null. However, Credentials is null and if you drill-down to the non-public members, there is a property webProxy where Address is null. So it seems that checking for null DefaultWebProxy does not always indicate that there is no proxy set.

Anyway, you also check to see if the proxyAddress equals the uri and it is, so in the end IsSystemProxySet returns false. So this method appears to be correct.

Oddly enough, I don't appear to be getting that SSL error anymore. Not sure what changed.

However, I did find a different problem. If you set your package source URL to http://localhost:57875/nuget (without trailing slash), the NuGet.Server will redirect to http://localhost:57875/nuget/ (with trailing slash). However AreCredentialsValid is not seeing the redirect, so it gets caught in a loop asking for credentials.

Hope this helps.

Jul 7, 2011 at 2:30 PM

@kiliman: Thanks for the update. I am glad that you are not seeing the SSL issue anymore. In regards to the redirecting issue, I remember seeing some comment made in some of the dev discussions saying that there was a class that can normalize Url(s) so maybe I can use that for fix up the Url so that we don't try to re-authenticate for this scenario.

Coordinator
Jul 7, 2011 at 3:55 PM

Not sure normalizing URLs would help in this case, would it?

I think technically, http://example.com/foo Is a different URL than: http://example.com/foo/

Whether an app redirects from one to another is an implementation detail of the site. But I could be wrong. Not in the mood to go looking up RFCs on it. J

Phil

Jul 7, 2011 at 11:48 PM

Phil,

I've quickly scanned through the RFC and I did not notice anything that says that the trailing slash makes it a different but I am not 100% sure since I did not do a thorough search. However I did run Fiddler and I did not notice any redirect headers.
I've also tried to see if the Uri class would treat the missing trailing slash as a different site location and it does not. What I can do is to change the way that I am caching the proxies and credentials based on the Uri.AbsoluteUri which should fix this problem.
Let me know if you can think of anything else.

Jul 8, 2011 at 2:02 PM

Actually the trailing slash is important, otherwise the client would not be able to tell if the last part is either a folder or a file. This would affect all relative references. That is why the server will redirect from /nuget to /nuget/.

Anyway, here is my Fiddler trace. NOTE: This is from my browser. I wasn't able to capture a trace inside VS. For some reason it won't go through Fiddler for localhost.

GET http://localhost:57875/nuget HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost:57875

HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic
Date: Fri, 08 Jul 2011 13:50:50 GMT
Content-Length: 4975

GET http://localhost:57875/nuget HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost:57875
Authorization: Basic *****************==

HTTP/1.1 307 Temporary Redirect
Cache-Control: private
Content-Length: 1813
Content-Type: text/html; charset=UTF-8
Location: http://localhost:57875/nuget/
Server: Microsoft-IIS/7.5

GET http://localhost:57875/nuget/ HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost:57875
Authorization: Basic *****************==

HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 397
Content-Type: application/xml;charset=utf-8
Server: Microsoft-IIS/7.5
DataServiceVersion: 1.0;
Date: Fri, 08 Jul 2011 13:50:59 GMT

Jul 8, 2011 at 2:13 PM

@kiliman: Based on you finding I would say that the current implementation is working correctly then because if it is a different Url then we should be prompting the user again for credentials. I have also updated my fork with the latest changed from the main fork and it's now going through a code review. So when you have some time and want to test with the latest bits you can do that at any time. I just want to make sure that we get as many testers running this feature as possible before we release it into the wild.

Jul 8, 2011 at 2:54 PM

Hmm.. I'm not sure if that is correct. If a user had to authenticate for each different URL, then they would get prompted for EVERY file since they are all different URLs.

I did some experimenting, and the way IE does it makes sense.

When I try to access /nuget/, it prompts me for credentials. There after, every request for /nuget/* will continue to use the same credentials. However, if I try to access just /nuget, it will re-prompt, since that URL is relative to ROOT. So IE re-uses credentials for a requests in a given sub-tree, but will prompt for requests in parent trees or sibling trees. This makes total sense.

Anyway, the issue I see with your fork is that even though it prompts for /nuget, it's either not seeing the redirect to /nuget/, or it continues to prompt for /nuget and gets caught in a loop.

BTW: when I was experimenting with my own fork for authentication, I wasn't setting WebRequest.Credentials with a specific credential. I was populating CredentialCache. This way WebRequest would send the proper credentials as needed.

Let me know when you update your fork and I'll test again.

Thanks!

Jul 9, 2011 at 2:33 PM

@kiliman: So couple of things after looking at the items that you've mentioned.

  1. The request for credentials would only happen once per Package Repository not per different package so if someone set's up a package repository that is pretty much the same except one is missing a trailing slash then I would say that that should not be part of the credential code but rather should be handled as part of validation logic when someone is adding the package repository.
  2. The other item of setting the CredentialCache instead of setting the request credentials. Where you creating and holding on to the CredentialCache object instance or were you talking about using the DefaultCredentials static property?
    If you are referring to the static property then we could potentially run into the same issue as the current implementation of VsWebProxy where they are always assuming that once someone authenticates that that's the only type of proxy that someone might want to go through
    which in most of the cases is OK but not very flexible if you needed to be able to do it against multiple proxies. This example might not be very common but if you are thinking about multiple nuget package repos then this scenario becomes very common where we want to authenticate
    with different credentials against different repos.

Hopefully I understood you correctly and please let me know if I did not as I would like to ensure that the implementation that I have is going to work in as many scenarios as possible.
I have also updated my fork with the very latest code changes and have finished implementing the dev review items and currently waiting for the pull request to be accepted so more people can start looking and testing the latest bits.

Feb 13, 2013 at 6:31 AM
Where did this end up? I am unable to get basic authentication to work on a private nuget server. http://username:password still comes back with a 401 and nuget.exe asks for username/password.
Mar 1, 2013 at 8:10 AM
Hi, any news? I need a solution to get packages from TeamCity feed without inserting each time username/password
Mar 1, 2013 at 12:11 PM
We added command to allow you to associate a pair of username/password for each feed. This information is stored in the %appdata%\nuget.config file, encrypted. After that, whenever you query a feed for packages, it will apply the credentials automatically.

Use the 'source' command to associate credentials.