title: ASP.NET Core Web API Authentication allowing unauthorized access
body: <p>I'm creating an authentication scheme for my ASP.NET Core API.  </p>
  
  <p>It calls my handler and hits the breakpoint just fine, but the API calls still return results even when the authorization fails.    </p>
  
  <pre><code>protected override async Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync()
  {
      if (!Request.Headers.ContainsKey(AuthorizationHeaderName))
      {
          //Authorization header not in request
          return AuthenticateResult.Fail("Missing Authorization header");
      }
  </code></pre>
  
  <p>In my naive understanding, it shouldn't return data if it fails authentication.    </p>
  
  <p>What am I missing?</p>
  
  <p>DETAILS      </p>
  
  <p>I register the scheme like this in Startup.ConfigureServices     </p>
  
  <pre><code>services.AddAuthentication(options =&gt; {
      // 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&lt;BasicAuthenticationOptions, BasicAuthenticationHandler&gt;("Basic", null);
  </code></pre>
  
  <p>Startup.Config calls     </p>
  
  <pre><code> app.UseAuthentication();
   app.UseHttpsRedirection();
   app.UseMvc();
  </code></pre>
  
  <p>The rest of the code looks like this:    </p>
  
  <pre><code>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&lt;AuthenticateResult&gt; HandleAuthenticateAsync();
      }
  
      public static class BasicAuthenticationDefaults
      {
          public const string AuthenticationScheme = "Basic";
      }
  
      public class BasicAuthenticationOptions : AuthenticationSchemeOptions
      { }
  
      public class BasicAuthenticationHandler : AuthenticationHandler&lt;BasicAuthenticationOptions&gt;
      {
          private const string AuthorizationHeaderName = "Authorization";
          private const string BasicSchemeName = "Basic";
  
          public BasicAuthenticationHandler(
              IOptionsMonitor&lt;BasicAuthenticationOptions&gt; options,
              ILoggerFactory logger,
              UrlEncoder encoder,
              ISystemClock clock)
              : base(options, logger, encoder, clock)
          {
          }
  
          protected override async Task&lt;AuthenticateResult&gt; 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);
          }
      }
  }
  </code></pre>
  
  <p>And here is the controller that is improperly returning results.</p>
  
  <pre><code>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&lt;IEnumerable&lt;string&gt;&gt; Get()
          {
              return new string[] { "value1", "value2" };
          }
      } 
  }
  </code></pre>
  
  <p>All these lines of code get hit in the debugger, so that part seems to be working correctly.</p>
  
  <p>But the API call is still returning results even though it fails authentication.</p>
  
  <p><strong>Update:</strong><br>
  Adding an AuthenticationScheme attribute to the Controller makes it fail.<br>
  Like this:      </p>
  
  <pre><code>[Route("api/[controller]")]
  [ApiController]
  [Authorize(AuthenticationSchemes = "Basic")]
  public class ValuesController : ControllerBase
  </code></pre>
  
  <p>This is no good. It should fail by default instead of having to add it to every controller.</p>
  
  <p><strong>Update 2:</strong> </p>
  
  <p>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.    </p>
  
  <p>I inherited from AuthorizeAttribute, using the idea from Matti Price, and IFilterMetadata, required by AddMvc. That compiles but still allows unauthorized access.     </p>
  
  <pre><code>public class BasicAuthorizeAttribute : AuthorizeAttribute, IFilterMetadata { }       
  
  services.AddMvc(options =&gt; {
      options.Filters.Add(typeof(BasicAuthorizeAttribute));
  }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  </code></pre>
  
  <p><strong>Update 3:</strong><br>
  Tried </p>
  
  <pre><code>policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser()
  </code></pre>
  
  <p>as suggested by Matti, but that returned    </p>
  
  <pre><code>InvalidOperationException: No authenticationScheme was specified, and there was no DefaultChallengeScheme found.
  </code></pre>
  
  <p>I don't have any interest in redirecting to a non-existent login page for an API, so I tried    </p>
  
  <pre><code>policy = new AuthorizationPolicyBuilder().AddAuthenticationSchemes(new[] {BasicAuthenticationDefaults.AuthenticationScheme })
  </code></pre>
  
  <p>That compiles but throws the exception     </p>
  
  <pre><code>InvalidOperationException Message=AuthorizationPolicy must have at least one requirement.
  </code></pre>