Reynevan
Reynevan

Reputation: 1545

How do I authorize requests based on the Authorization header value?

I am trying to come up with a simple API with ASP.NET Core and I would like to limit access to some endpoints based on the authorization header of the request.

In ASP.NET MVC5 I was able to do it by inheriting from the AuthorizationFilterAttribute and overriding the check method,m like so:

public class BasicAuthorizationAttribute : AuthorizationFilterAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        try
        {
            if (actionContext.Request.Headers.Authorization == null)
            {
                actionContext.Response = actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                return;
            }

            var authorizationToken = Encoding.UTF8.GetString(Convert.FromBase64String(actionContext.Request.Headers.Authorization.Parameter)).Split(':');
            var username = authorizationToken[0];
            var password = authorizationToken[1];
            if (SecurityHelper.Login(username, password))
            {
                Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(username), null);
                actionContext.RequestContext.Principal = Thread.CurrentPrincipal;
            }
            else
            {
                actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
                return;
            }
        } catch
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
            return;
        }
    }
}

But from what it seems, Core uses an entirely different authorization system, which seems to require much more code and the setup doesn't seem like it's meant to be used with header checks. The entire Policy system seems like it's overcomplicated for this case. Is that what I should be using for this case? Or is there some built-in alternative for checking basic auth?

Thank you!

Upvotes: 2

Views: 8814

Answers (3)

Jan W
Jan W

Reputation: 474

Simple header authentication in .net core I came up with based on IAsyncActionFilter.

public class XAuthConfiguration
{
    public string AuthHeaderKey { get; set; }
    public string AuthHeaderValue { get; set; }
}

public class XAuthFilter : IAsyncActionFilter
{
    protected readonly XAuthConfiguration XAuthConfiguration;

    //Inject configuration
    public XAuthFilter(IOptions<XAuthConfiguration> config)
    {
        XAuthConfiguration = config.Value;
    }
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {

        if (context.HttpContext.Request.Headers.TryGetValue(XAuthConfiguration.AuthHeaderKey, out var headers))
        {
            if (headers.Contains(XAuthConfiguration.AuthHeaderValue))
            {
                //move along...
                await next();
            }
        }

        context.Result = new UnauthorizedResult();

    }
}

Then you have to decorate your controller accordingly:

    [HttpGet]
    [ServiceFilter(typeof(XAuthFilter))]
    [Route("DoSomething")]
    public bool DoSomenthing([FromHeader(Name = "x-auth")][Required] string auth)
    {
        return service.DoSomenthing();
    }

Finally some touchup code in Startup.cs and voila.

//Inject configuration from appsettings.json section
builder.Services.Configure<XAuthConfiguration>(builder.Configuration.GetSection("XAuthConfiguration"));

//Inject custom XAuthFilter
builder.Services.AddScoped<XAuthFilter>();

Upvotes: 0

rfcdejong
rfcdejong

Reputation: 2320

Injecting the IHttpContextAccessor in the Constructor is not needed.

You can access the HttpContext by doing

if (context.Resource is HttpContext httpContext)
{
    ...
}

or for MVC

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

Source: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0#access-mvc-request-context-in-handlers

Upvotes: 4

Nan Yu
Nan Yu

Reputation: 27538

You needn't to add header as claims , you can access the authorization header by injecting an instance of an IHttpContextAccessor into your AuthorizationHandler :

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    IHttpContextAccessor _httpContextAccessor = null;
    public MinimumAgeHandler(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                    MinimumAgeRequirement requirement)
    {

        HttpContext httpContext = _httpContextAccessor.HttpContext;

        string authHeader = httpContext.Request.Headers["Authorization"];
        if (authHeader != null && authHeader.StartsWith("Basic "))
        {
            // Get the encoded username and password
            var encodedUsernamePassword = authHeader.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries)[1]?.Trim();
            // Decode from Base64 to string
            var decodedUsernamePassword = Encoding.UTF8.GetString(Convert.FromBase64String(encodedUsernamePassword));
            // Split username and password
            var username = decodedUsernamePassword.Split(':', 2)[0];
            var password = decodedUsernamePassword.Split(':', 2)[1];
            // Check if login is correct

        }

        ........

        if (.....)
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

You may need to register this in your DI setup , as follows:

services.AddHttpContextAccessor();

Upvotes: 10

Related Questions