40

Closed

Authentication support for private repositories

description

As i got the state of the current code base it is not possible to authenticate through any HTTP supported mechanisms (Basic+SSL/NTLM/....).
The current architecture does not make it easy to integrate such a feature. The console window in Visual Studio does not allow to call unmanaged code as the credui standard networking credentials dialog.

I think it would be a great addition especially to corporate environments to be able to authenticate against a repository.

Are you interested in starting the discussion around this topic? Are there any existing plans?

Best regards,
Daniel
Closed Aug 26, 2011 at 8:45 PM by aldion
Verified

Using diverse authentication scheme
   * Anynomous (no prompt required)
   * Basic 
      > filed : #1402
   * Digest 
       > Unsupported : filed #1424
   * Windows
   * Combining Allow/Deny 
      > filed #1444
   * using HTTP/HTTPS 
      > filed #1435
   * Proxy 
        - proxy requiring auth 
        - proxy + auth
verified the following:
- VS Nuget Client
   * multiple feeds
     > #1422 - Auth : multiple feed from the same server are hard to discern 
     > #1421 - Auth giving "The underlying connection was closed : an unexpected error occured on a received" 
    * installation (recent, online, installed), update, uninstall
      > #1404 - Online/Recent tab : Installing a package requiring auth may lock the dialogs 
      > #1436 - Auth blocks installation of installed/recent packages (from feeds not requiring auth)
  • nuget.exe
     * push/install/update
        > #1413 - nuget.exe install packages.config from Basic Auth source prompts for password for each package if you have a trailing slash. 
        > #1416 - nuget.exe update with auth repository doesn't provide error message for bad credentials 
       > #1429 - nuget.exe auth doesn't work with repository requiring Windows Authentication 
    
  • misc
    > #1433 - When testing feeds through fiddler I get "The remote name could not be resolved:'ipv4.fiddler'"

comments

dotnetjunky wrote Nov 10, 2010 at 6:29 AM

If we want to support authentication, the best place to do it is in the Options UI.

lennybacon wrote Nov 10, 2010 at 10:29 AM

After I thought a while on the topic the easiest way to get is going on the current code base would be to include authentication information in the url of the feed as entered in the UI.

e.g.: http://{username}:{password}@{server}/odata/v1

This requires only a small change to the HttpRequestor class:

namespace NuGet {
internal static class HttpWebRequestor {

//...

    public static WebResponse GetResponse(Uri uri)
    {
        var credInfo = uri.InferCredentials();
        WebRequest request = WebRequest.Create(credInfo.Key);
        InitializeRequest(request);
        if (credInfo.Value != null)
        {
            request.Credentials = credInfo.Value;
        }
        return request.GetResponse();
    }   

//...

}

public static class UriExtensions
{
    public static KeyValuePair<Uri, ICredentials> InferCredentials(
        this Uri uri)
    {
        var authEvidence = string.Concat("@", uri.Authority);
        var uriStr = uri.OriginalString;
        if (uriStr.IndexOf(authEvidence) == -1)
        {
            return new KeyValuePair<Uri, ICredentials>(uri, null);
        }

        var schemeLength = uri.Scheme.Length + 3;

        var hostIndex = uriStr.IndexOf(authEvidence);
        var userNameAndPasswordPart =
            uriStr.Substring(schemeLength, hostIndex - schemeLength);
        var userNameAndPasswordParts = userNameAndPasswordPart.Split(':');
        var userName = userNameAndPasswordParts[0];
        var password = userNameAndPasswordParts[1];

        var newUri =
            new Uri(
                string.Concat(
                    uriStr.Substring(0, uri.Scheme.Length + 3),
                    uriStr.Substring(hostIndex + 1)));

        return new KeyValuePair<Uri, ICredentials>(
            newUri,
            new NetworkCredential(userName, password));
    }
}   
}

I just commited the changes to my fork https://hg01.codeplex.com/forks/lennybacon/lennybaconnugetcontrib. The fork already has a running Pull-Request.

Haacked wrote Nov 10, 2010 at 4:01 PM

Lenny, can you submit a code review for your changes? http://nuget.codeplex.com/wikipage?title=Code%20Reviews

akronite wrote Nov 23, 2010 at 2:25 AM

I have a similar issue, don't know if they are related but here are the details:

Install-Package : <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>ERROR: Cache Access Denied</title>
<style type="text/css"><!--
/*
Stylesheet for Squid Error pages
Adapted from design by Free CSS Templates
http://www.freecsstemplates.org
Released for free under a Creative Commons Attribution 2.5 License
*/

/ Page basics /
  • {
    font-family: verdana, sans-serif;
    }
html body {
margin: 0;
padding: 0;
background: #efefef;
font-size: 12px;
color: #1e1e1e;
}

/ Page displayed title area /

titles {

margin-left: 15px;
padding: 10px;
padding-left: 100px;
background: url('http://www.squid-cache.org/Artwork/SN.png') no-repeat left;
}

/ initial title /

titles h1 {

color: #000000;
}

titles h2 {

color: #000000;
}

/ special event: FTP success page titles /

titles ftpsuccess {

background-color:#00ff00;
width:100%;
}

/ Page displayed body content area /

content {

padding: 10px;
background: #ffffff;
}

/ General text /
p {
}

/ error brief description /

error p {

}

/ some data which may have caused the problem /

data {

}

/ the error message received from the system or other software /

sysmsg {

}

pre {
font-family:sans-serif;
}

/ special event: FTP / Gopher directory listing /

dirmsg {

font-family: courier;
color: black;
font-size: 10pt;
}

dirlisting {

margin-left: 2%;
margin-right: 2%;
}

dirlisting tr.entry td.icon,td.filename,td.size,td.date {

border-bottom: groove;
}

dirlisting td.size {

width: 50px;
text-align: right;
padding-right: 5px;
}

/ horizontal lines /
hr {
margin: 0;
}

/ page displayed footer area /

footer {

font-size: 9px;
padding-left: 10px;
}


body
:lang(fa) { direction: rtl; font-size: 100%; font-family: Tahoma, Roya, sans-serif; float: right; }
:lang(he) { direction: rtl; float: right; }
--></style>
</head>
<body>
<div id="titles"> <h1>ERROR</h1> <h2>Cache Access Denied.</h2> </div> <hr> <div id="content"> <p>The following error was encountered while trying to retrieve the URL: <a href="http://feed.nuget.org/ctp2/odata/v1/Packages()?">htt p://feed.nuget.org/ctp2/odata/v1/Packages()?</a></p> <blockquote id="error"> <p><b>Cache Access Denied.</b></p> </blockquote> <p>Sorry, you are not currently allowed to request http://feed.nuget.org/ctp2/odata/v1/Packages()? from this cache until you have auth enticated yourself.</p> <p>Please contact the <a href="mailto:root?subject=CacheErrorInfo%20-%20ERR_CACHE_ACCESS_DENIED&amp;body=CacheHost%3A%20au-glb-pxy04.s kmconsulting.com%0D%0AErrPage%3A%20ERR_CACHE_ACCESS_DENIED%0D%0AErr%3A%20%5Bnone%5D%0D%0ATimeStamp%3A%20Tue,%2023%20Nov%202010%2003%3A 22%3A22%20GMT%0D%0A%0D%0AClientIP%3A%20172.20.23.214%0D%0A%0D%0AHTTP%20Request%3A%0D%0AGET%20%2Fctp2%2Fodata%2Fv1%2FPackages()%3F$filt er%3Dtolower(Id)%2520eq%2520%27bootstrapper%27%20HTTP%2F1.1%0AUser-Agent%3A%20Microsoft%20ADO.NET%20Data%20Services%0D%0ADataServiceVe rsion%3A%201.0%3BNetFx%0D%0AMaxDataServiceVersion%3A%202.0%3BNetFx%0D%0AAccept%3A%20application%2Fatom+xml,application%2Fxml%0D%0AAcce pt-Charset%3A%20UTF-8%0D%0AProxy-Authorization%3A%20NTLM%20TlRMTVNTUAABAAAAB4IIogAAAAAAAAAAAAAAAAAAAAAFASgKAAAADw%3D%3D%0D%0ACache-Con trol%3A%20max-age%3D0%0D%0APragma%3A%20no-cache%0D%0AHost%3A%20feed.nuget.org%0D%0AProxy-Connection%3A%20Keep-Alive%0D%0A%0D%0A%0D%0A" >cache administrator</a> if you have difficulties authenticating yourself or <a href="http://au-glb-pxy04.skmconsulting.com/cgi-bin/ch passwd.cgi">change</a> your default password.</p> <br> </div> <hr> <div id="footer"> <p>Generated Tue, 23 Nov 2010 03:22:22 GMT by au-glb-pxy04.skmconsulting.com (squid/3.1.7)</p> <!-- ERR_CACHE_ACCESS_DENIED --> </div> </body></html>

At line:1 char:16
  • install-package <<<< BootStrapper
    • CategoryInfo : NotSpecified: (:) [Install-Package], DataServiceClientException
    • FullyQualifiedErrorId : NuGet.VisualStudio.Cmdlets.InstallPackageCmdlet

DaveSussman wrote Apr 21, 2011 at 10:30 AM

Authenticated package sources is really important for me. I want a public package feed for some of the software I supply, but only to authorised clients.

maartenba wrote May 31, 2011 at 9:27 AM

This would be a huge step foward for our www.myget.org "private feed as a service". We would love to offer a real private feed protected by either basic auth of, which would be even better, by using Windows Identity Foundation. +1!

Haacked wrote May 31, 2011 at 3:50 PM

I think we have a pull request for this. We'll look into it. It's possible for the author of a pull request to add others to it to help out, so if it needs more work, please consider pitching in.

Kiliman wrote May 31, 2011 at 6:30 PM

@Haacked, I don't see an active pull request for this, so I'm not sure if somebody is working on it. I figured I'll give it a go since it'll impact the Add Package Source dialog as well.

Anyway, I did a quick test and figured out how to add support for Basic Authentication. I setup a test NuGet.Server and added the package SimpleBasicAuthenticationModule by Sander van de Velde (http://nuget.org/List/Packages/SimpleBasicAuthenticationModule). Nice package by the way.

I verified that when I navigated to http://localhost/nuget/Packages that it prompted me for credentials. When I tried the command line: nuget list -source http://locahost/nuget I got the error (401) Unauthorized which is what I expected.

I then modified NuGet.HttpClient and added a CredentialCache that mapped my URL with the correct credentials. This was bound to the request in InitializeRequest(). I hard coded for the test.

I re-ran the nuget list command and sure enough, it authenticated and returned the package list. Sweet!

I'm now in the process of updating the Add Package Source dialog to allow the user to specifiy credentials. Those will be encrypted and stored in NuGet.config. This will then be used to build the credential cache.

Once I'm finished, I'll send a pull request for review.

Let me know if you have any thoughts.

Kiliman

dotnetjunky wrote May 31, 2011 at 7:03 PM

Cool. Can you describe what the UI will be like?

TripleEmcoder wrote May 31, 2011 at 7:15 PM

@Kiliman - this sounds really nice. So for VS, the credentials would be set in the "Package Sources" dialog? How about nuget.exe? Something similar SetApiKey? I think we need to figure out how this authentication relates to the publishing API key. I doesn't seem right to same a login/password for read access and an API key for write access.

Kiliman wrote May 31, 2011 at 7:17 PM

I was thinking of adding a "Connect as..." button next to the Source. This would pop up a dialog to enter the username/password. I would also add a "lock" icon next to secured package sources.

I'll attach a screenshot when I get something usable.

dotnetjunky wrote May 31, 2011 at 7:39 PM

And the dialog will be the Windows-provided credentials dialog, right? We already have one in the code base, in the PowerShellHost project (NativeMethods.cs). We need to factor that out somewhere else to reuse it in the Options project.

Also, please make sure the code change will work with Integrated Authentication too. I think that's the more common case in enterprise environment.

Kiliman wrote May 31, 2011 at 7:43 PM

@TripleEmcoder, The credentials are stored in NuGet.config which is also used by the nuget command line. So if you've already added a package source URL with credentials in VS, then it'll use the correct credentials even from the command line.

However, if you don't have VS installed (e.g, a CI server) then yeah, I see where you'll need something like a SetCredentials command in nuget. I'll add that to my TODO list.

As for how this relates to APIKey, I think they're separate use cases. I think the primary use for the new authentication support is to only grant access to certain users even if the NuGet repo is publically accessible. For example, DevExpress embeds an authentication token in the URL to restrict access to its customers. With authentication, they can simplify this whole process.

Also the new MyGet project (myget.org) would benefit as well by authenticating users to their own private feeds.

Anyway, keep the comments and suggestions coming.

Thanks!

sjnaughton wrote May 31, 2011 at 8:11 PM

I really like this it would open up manny possibilities.

Haacked wrote May 31, 2011 at 9:27 PM

@Kiliman, let's not store credentials in nuget.config. We already have support for authenticating proxies which uses the Windows credential store for properly prompting and storing credentials. Ilyalozovyy was working on implementing this feature, so please start a discussion with him. I don't want your two changes to step on each others.

ilyalozovyy wrote May 31, 2011 at 10:11 PM

@haacked & @Kiliman I am now back from my vacation and I am back on finishing up the Proxy support for the last step which is Visual Studio package.
I was hoping that we could refactor the proxy functionality to re-use it's basic framework and use it for this feature which I've already talked about with @davidfowl

Haacked wrote May 31, 2011 at 10:45 PM

Ok, we just discussed this over here and we agree that Proxy support is a bit different than Authentication support. Proxy support depends on what network you happen to be joined to. That can change as you take your laptop to different places for example.

But authentication information is strictly tied to the specific package feed and is unlikely to change. So it does make sense to store auth credentials with the package source rather than requiring prompting.

Kiliman, can you start a discussion with the proposed changes and a mock-up of the UI? It's easier for people to join a discussion than to comment on the issue (Issues don't allow responses via email).

Oh, and welcome back! :)

ilyalozovyy wrote Jun 1, 2011 at 12:01 AM

@haacked there seems to be an existing discussion that was started and we can use that to continue this discussion: http://nuget.codeplex.com/discussions/237995
I would also like to comment on the proxy support and the mentioned difference between the proxy and repository authentication. The functionality that they provide is different
however the feature of discovering credentials is not. The way that the credentials are stored and retrieved for the proxy functionality can be totally re-used for the authentication with a small bit of refactoring
by simply changing and possibly re-naming the ProxyFinder to return the credentials for the given url instead of returning the IWebProxy this way we can re-use as much of the code base as possible especially because most of the
code needs to be duplicated in things like nuget.exe, bootstrapper, and the now "excluded" Package Explorer project :)

lennybacon wrote Jun 1, 2011 at 8:18 AM

But... It's a security topic.
Thanks for the loop. Any suggestions that bring us further?

ilyalozovyy wrote Jun 1, 2011 at 11:58 AM

@lennybacon see my comments in the discussion thread: http://nuget.codeplex.com/discussions/237995

lennybacon wrote Jun 1, 2011 at 12:03 PM

@ilyalozovyy: Sounds great!

maartenba wrote 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"

maartenba wrote Jun 29, 2011 at 10:23 AM

FYI: I've also created a fork which contains an updated NuGet.Server project which also enables support for this.

dotnetjunky wrote Jul 9, 2011 at 4:03 PM