简体   繁体   中英

ASP.NET Core Web API Authentication allowing unauthorized access

I'm creating an authentication scheme for my ASP.NET Core API.

It calls my handler and hits the breakpoint just fine, but the API calls still return results even when the authorization fails.

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
    if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
    {
        //Authorization header not in request
        return AuthenticateResult.Fail("Missing Authorization header");
    }

In my naive understanding, it shouldn't return data if it fails authentication.

What am I missing?

DETAILS

I register the scheme like this in Startup.ConfigureServices

services.AddAuthentication(options => {
    // This (options.Default..Scheme) causes the default authentication scheme to be set.
    // Without this, the Authorization header is not checked and
    // you'll get no results. 
   options.DefaultAuthenticateScheme = BasicAuthenticationDefaults.AuthenticationScheme;
}).AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("Basic", null);

Startup.Config calls

 app.UseAuthentication();
 app.UseHttpsRedirection();
 app.UseMvc();

The rest of the code looks like this:

using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;

namespace WebAPI.Authentication
{
    public interface IBasicAuthenticationService
    {
        Task<AuthenticateResult> HandleAuthenticateAsync();
    }

    public static class BasicAuthenticationDefaults
    {
        public const string AuthenticationScheme = "Basic";
    }

    public class BasicAuthenticationOptions : AuthenticationSchemeOptions
    { }

    public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
    {
        private const string AuthorizationHeaderName = "Authorization";
        private const string BasicSchemeName = "Basic";

        public BasicAuthenticationHandler(
            IOptionsMonitor<BasicAuthenticationOptions> options,
            ILoggerFactory logger,
            UrlEncoder encoder,
            ISystemClock clock)
            : base(options, logger, encoder, clock)
        {
        }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
            {  // Rejected here. Should fail.
                //Authorization header not in request
                return AuthenticateResult.Fail("Missing Authorization header");
            }

            if ....  // never gets this far
            }

            return AuthenticateResult.Success(ticket);
        }
    }
}

And here is the controller that is improperly returning results.

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;

namespace TMAWebAPI.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public ActionResult<IEnumerable<string>> Get()
        {
            return new string[] { "value1", "value2" };
        }
    } 
}

All these lines of code get hit in the debugger, so that part seems to be working correctly.

But the API call is still returning results even though it fails authentication.

Update:
Adding an AuthenticationScheme attribute to the Controller makes it fail.
Like this:

[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = "Basic")]
public class ValuesController : ControllerBase

This is no good. It should fail by default instead of having to add it to every controller.

Update 2:

Adding a filter to services.AddMvc looks promising, but that doesn't work either. Documentation claims that you don't have to implement an Authorization filter since they are included. Not that I can find.

I inherited from AuthorizeAttribute, using the idea from Matti Price, and IFilterMetadata, required by AddMvc. That compiles but still allows unauthorized access.

public class BasicAuthorizeAttribute : AuthorizeAttribute, IFilterMetadata { }       

services.AddMvc(options => {
    options.Filters.Add(typeof(BasicAuthorizeAttribute));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Update 3:
Tried

policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser()

as suggested by Matti, but that returned

InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

I don't have any interest in redirecting to a non-existent login page for an API, so I tried

policy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(new[] {BasicAuthenticationDefaults.AuthenticationScheme })

That compiles but throws the exception

InvalidOperationException Message=AuthorizationPolicy must have at least one requirement.

You'll need to add the [Authorize] attribute to your controllers to cause the authorization to actually do anything with it's result. You can add it globally like this :

services.AddMvc(config =>
{
    var policy = new AuthorizationPolicyBuilder()
                 .RequireAuthenticatedUser()
                 .Build();
    config.Filters.Add(new AuthorizeFilter(policy));
});

The final resolution uses AddMVC. The solution was that in addition to adding a scheme, the scheme needed a requirement.

This works-ish. If I don't send any auth header, it returns an empty page. If I send an expired header, then it sends 500. Should be 401. But it doesn't return any values and that's really all I care about.

public class TokenAuthorizationRequirement: IAuthorizationRequirement {}

services.AddMvc(config => {
   var policy = new AuthorizationPolicyBuilder()
      .AddAuthenticationSchemes(new[] {BasicAuthenticationDefaults.AuthenticationScheme })
      .AddRequirements(new BasicAuthorizationRequirement())
      .Build();
   config.Filters.Add(new AuthorizeFilter(policy));
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM