Building Custom NuGet Feeds

Share this post:

Most .NET Development shops have a tendency to build up a lot of code over the years. Naturally many of our clients that have been around for a while have some pretty horrible ways of managing code because how on earth do you make it easier share code from one developer to another or one team to another? Perhaps one of the most amusing things is that we already knew the answer because we use it everyday as .NET developers. The answer of course is NuGet. Now of course putting closed source, proprietary code up on NuGet.org would be a horrible idea, but the NuGet toolchain doesn't require us to be tightly coupled to NuGet.org.

Now the purpose of this blog is not to teach you fundamentals of packaging your libraries into packages. There are some undeniable facts however. For starters if you are packaging your libraries into NuGet packages then you are implementing some sort of Versioning strategy which can be particularly helpful for diagnosing bugs and regressions. What version was it when the bug was introduced? When you are packaging your libraries you don't stay on version 1.0.0.0 for 20 years (and yes we have seen many shops doing exactly this).

Ok so we know we need a feed, but we don't want to use GitHub packages, or Azure DevOps Artifacts... we want something that we can control. This is often a much more common scenario than it may seem at first glance. It's something that I use personally for Sponsor Connect, it's something that we use for Enterprise customers who are looking for access to private builds of Prism or some of our other tools. This concept is something that component vendors such as Telerik and Infragistics use with private feeds that authenticate their customers.

Frankly though we found it tiring that it was such a heavy lift to get your own NuGet feed. Yes there are some options out there. If you're working within your intranet you could even stand up a host running BaGet and provide an unauthenticated feed that will replace your need to use nuget.org entirely if you want. BaGet is a great project, but it is lacking for the scenarios we kept running into. The feed needs to be public, we need to authenticate the user, and further we need to know if they have publishing rights, can they access a specific package, and finally what are our users doing? After discussing this with some of our friends in the industry we've decided to openly share the base infrastructure of what we use for Sponsor Connect, and our Enterprise feeds. This is all open source and now available on NuGet.org to make it easy for you to include in your AspNetCore app and quickly standup a customized NuGet feed with your own Authentication logic.

Getting Started

AvantiPoint Packages is super simple to get started with, and can be used as either an authenticated or unauthenticated NuGet feed. What sets AvantiPoint Packages apart though is how easy it is to implement your own custom Authentication for Package Publishers and Package Consumers, as well as to hook into events such as Package/Symbols Uploaded/Downloaded, and even provide custom package specific logic to determine if someone should have access to download a specific package.
    public class PackageAuthenticationService : IPackageAuthenticationService
{
    public async Task<NuGetAuthenticationResult> AuthenticateAsync(string apiKey, CancellationToken cancellationToken)
    {
        // Your logic goes here
        // You should validate that the user has Package Publishing privileges
    }

    public async Task<NuGetAuthenticationResult AuthenticateAsync(string username, string token, CancellationToken cancellationToken)
    {
        // Your logic goes here
    }
}
  

As you can see it's a very simple interface that you can implement to provide your own custom authentication logic. This is split into two methods. The first is meant for legacy NuGet ApiKey authentication. This is currently only supported for Package Publishing. The second is used for Basic user authentication and should be used for Package consumers. A good practice that you should employ when creating the NuGetAuthenticationResult is to create a ClaimsPrincipal. This will allow you easily access any claims you would like to set on the user later as we respond to events the authenticated user is doing.

For instance let's look at a scenario that our friends at Telerik or Infragistics might employ as they both maintain private NuGet feeds for their customers. Each company offers controls for Desktop, Web, & Mobile development with licenses that might grant you access to one or more of those control suites. Luckily this is a scenario where AvantiPoint Packages shines as it is really quite easy to grant users access to only download packages they actually should have access to. Building on our Authentication with a ClaimsPrincipal we constructed and passed back in the Authentication Result we might have something like the following.

    public class NuGetFeedActionHandler : INuGetFeedActionHandler
{
    public NuGetFeedActionHandler(IHttpContextAccessor contextAccessor)
    {
        HttpContext = contextAccessor.HttpContext;
    }

    private HttpContext HttpContext { get; }
    private ClaimsPrincipal => HttpContext?.User;

    public async Task<bool> CanDownloadPacakge(string packageId, string version)
    {
        // Your validation logic here...
    }
}
  

While this is interesting really there are a few other events that may be of more use to us. Let's look next at Uploads. AvantiPoint Packages provides separate events for Packages and Symbols uploads. So let's see how we might handle these... 

    public class NuGetFeedActionHandler : INuGetFeedActionHandler
{
    public async Task OnPackageUpload(string packageId, string version)
    {
        // Email User confirmation of package upload
    }

    public async Task OnSymbolsUpload(string packageId, string version)
    {
        // Email User confirmation of symbols upload
    }
}
  

You really have fine grain control over how to handle any of these events, and the best part is that you can focus only on what you want to do here. 

Setting up your NuGet Feed

To set up your own NuGet feed from scratch is actually pretty simple.
  1. Create a new empty AspNetCore 5.0 WebApi project
  2. Install the latest AvantiPoint.Packages.Hosting nuget
  3. Install the latest AvantiPoint.Packages.Database.{provider} nuget
  4. Optionally install the Aws or Azure package to use Cloud storage with AvantiPoint Packages
  5. Update your Startup
    public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddNuGetPackageApi(options => {
            // Or use Azure or Aws Cloud storage
            options.AddFileStorage();

            // Or use another Database provider
            options.AddSqlServerDatabase("DefaultConnection");
        });
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseEndpoints(endpoints => {
            endpoints.MapNuGetApiRoutes();
        });
    }
}
  

Have issues or want to get more information? Be sure to check out the GitHub repository.

Categories: .net
Tags: aspnetcore nuget
Share this post:

Comments

Be the first to leave a comment


Leave a Comment