OdeToCode IC Logo

Authorization Policies and Middleware in ASP.NET 5

Tuesday, October 6, 2015

Imagine you want to protect a folder full of static assets in the wwwroot directory of an ASP.NET 5 project. There are several different approaches you could take to solve the problem, but here is one flexible solution using authorization policies and middleware.

Services

First, in the Startup class for the application, we will add the required services.

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("Authenticated", policy => policy.RequireAuthenticatedUser());
    });
}

For the default authorization service we’ll make a named policy available, the Authenticated policy. A policy can contain any number of requirements allowing you to check claims and identities. In this code we will ultimately be using the built-in DenyAnonymousAuthorizationRequirement, because this is the type of requirement returned by the RequireAuthenticatedUser method. But again, you could make the requirement verify any number of characteristics about the user and the request.

The name Authenticated is important, because we will refer to this policy when authorizing users for access to a protected folder.

Middleware

Next, let’s write a piece of middleware named ProtectFolder and start with an options class to parameterize the middleware.

public class ProtectFolderOptions
{
    public PathString Path { get; set; }
    public string PolicyName { get; set; }
}

There is also the obligatory extension method to add the middleware to the pipeline.

public static class ProtectFolderExtensions
{
    public static IApplicationBuilder UseProtectFolder(
        this IApplicationBuilder builder, 
        ProtectFolderOptions options)
    {
        return builder.UseMiddleware<ProtectFolder>(options);
    }
}

Then the middlware class itself.

public class ProtectFolder
{
    private readonly RequestDelegate _next;
    private readonly PathString _path;
    private readonly string _policyName;
   
    public ProtectFolder(RequestDelegate next, ProtectFolderOptions options)
    {
        _next = next;
        _path = options.Path;
        _policyName = options.PolicyName;
    }

    public async Task Invoke(HttpContext httpContext, 
                             IAuthorizationService authorizationService)
    {
        if(httpContext.Request.Path.StartsWithSegments(_path))
        {
            var authorized = await authorizationService.AuthorizeAsync(
                                httpContext.User, null, _policyName);
            if (!authorized)
            {
                await httpContext.Authentication.ChallengeAsync();
                return;
            }
        }

        await _next(httpContext);
    }
}

The Invoke method on a middleware object is injectable, so we’ll ask for the current authorization service and use the service to authorize the user if the current request is heading towards a protected folder. If authorization fails we use the authentication manager to challenge the user, which typically redirects the browser to a login page, depending on the authentication options of the application.

Pipeline Configuration

Back in the application’s Startup class, we’ll configure the new middleware to protect the /secret directory with the “Authenticated” policy.

public void Configure(IApplicationBuilder app)
{
    app.UseCookieAuthentication(options =>
    {
        options.AutomaticAuthentication = true;
    });

    app.UseProtectFolder(new ProtectFolderOptions
    {
        Path = "/Secret",
        PolicyName = "Authenticated"
    });

    app.UseStaticFiles();

    // ... more middleware
}

Just make sure the protection middleware is in place before the middleware to serve static files.