title: ASP.NET Core 2.0 app.UseAuthentication not executing for every request
body: <p>I have a really weird issue which I have spent an entire day debugging and nowhere close to solving. I am in the process of upgrading my application from ASP.NET Core 1.x to 2.1. As part of doing this, I am having to re-wire the Authentication and Authorization mechanism. We use JWTBearer Authentication, and I am using postman to fire an API call, which executes the pipeline and I can see the AuthHandler executing. However if I fire the same request again, the AuthHandler does not execute and debugger "steps-over" the "context.AuthenticateAsync" call and returns the previous result. For the sake of elaborating, I have written a custom auth handler which is a copy paste of JWTAuthHandler. The code to create a custom handler is based off the <a href="https://stackoverflow.com/questions/45805411/asp-net-core-2-0-authentication-middleware">answer</a> here.</p>
  
  <pre><code>using Microsoft.AspNetCore.Authentication.JwtBearer;
  public class CustomAuthOptions : JwtBearerOptions
  {
  }
  
  using Microsoft.AspNetCore.Authentication;
  
  public static class CustomAuthExtensions
  {
      public static AuthenticationBuilder AddCustomAuth(this AuthenticationBuilder builder, Action&lt;CustomAuthOptions&gt; configureOptions)
      {
          return builder.AddScheme&lt;CustomAuthOptions, CustomAuthHandler&gt;("CustomScheme", configureOptions);
      }
  }
  
  
  public class CustomAuthHandler : AuthenticationHandler&lt;CustomAuthOptions&gt;
  {
      private OpenIdConnectConfiguration _configuration;
  
      public CustomAuthHandler(IOptionsMonitor&lt;CustomAuthOptions&gt; options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
      {
      }
  
      /// &lt;summary&gt;
      /// The handler calls methods on the events which give the application control at certain points where processing is occurring. 
      /// If it is not provided a default instance is supplied which does nothing when the methods are called.
      /// &lt;/summary&gt;
      protected new JwtBearerEvents Events
      {
          get =&gt; (JwtBearerEvents)base.Events;
          set =&gt; base.Events = value;
      }
  
      protected override Task&lt;object&gt; CreateEventsAsync() =&gt; Task.FromResult&lt;object&gt;(new JwtBearerEvents());
  
      /// &lt;summary&gt;
      /// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using &lt;see cref="TokenValidationParameters"/&gt; set in the options.
      /// &lt;/summary&gt;
      /// &lt;returns&gt;&lt;/returns&gt;
      protected override async Task&lt;AuthenticateResult&gt; HandleAuthenticateAsync()
      {
          string token = null;
          try
          {
              // Give application opportunity to find from a different location, adjust, or reject token
              var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options);
  
              // event can set the token
              await Events.MessageReceived(messageReceivedContext);
              if (messageReceivedContext.Result != null)
              {
                  return messageReceivedContext.Result;
              }
  
              // If application retrieved token from somewhere else, use that.
              token = messageReceivedContext.Token;
  
              if (string.IsNullOrEmpty(token))
              {
                  string authorization = Request.Headers["Authorization"];
  
                  // If no authorization header found, nothing to process further
                  if (string.IsNullOrEmpty(authorization))
                  {
                      return AuthenticateResult.NoResult();
                  }
  
                  if (authorization.StartsWith("CustomAuth ", StringComparison.OrdinalIgnoreCase))
                  {
                      token = authorization.Substring("CustomAuth ".Length).Trim();
                  }
  
                  // If no token found, no further work possible
                  if (string.IsNullOrEmpty(token))
                  {
                      return AuthenticateResult.NoResult();
                  }
              }
  
              if (_configuration == null &amp;&amp; Options.ConfigurationManager != null)
              {
                  _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted);
              }
  
              var validationParameters = Options.TokenValidationParameters.Clone();
              if (_configuration != null)
              {
                  var issuers = new[] { _configuration.Issuer };
                  validationParameters.ValidIssuers = validationParameters.ValidIssuers?.Concat(issuers) ?? issuers;
  
                  validationParameters.IssuerSigningKeys = validationParameters.IssuerSigningKeys?.Concat(_configuration.SigningKeys)
                      ?? _configuration.SigningKeys;
              }
  
              List&lt;Exception&gt; validationFailures = null;
              SecurityToken validatedToken;
              foreach (var validator in Options.SecurityTokenValidators)
              {
                  if (validator.CanReadToken(token))
                  {
                      ClaimsPrincipal principal;
                      try
                      {
                          principal = validator.ValidateToken(token, validationParameters, out validatedToken);
                      }
                      catch (Exception ex)
                      {
                          ////Logger.TokenValidationFailed(ex);
  
                          // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
                          if (Options.RefreshOnIssuerKeyNotFound &amp;&amp; Options.ConfigurationManager != null
                              &amp;&amp; ex is SecurityTokenSignatureKeyNotFoundException)
                          {
                              Options.ConfigurationManager.RequestRefresh();
                          }
  
                          if (validationFailures == null)
                          {
                              validationFailures = new List&lt;Exception&gt;(1);
                          }
                          validationFailures.Add(ex);
                          continue;
                      }
  
                      ////Logger.TokenValidationSucceeded();
  
                      var tokenValidatedContext = new TokenValidatedContext(Context, Scheme, Options)
                      {
                          Principal = principal,
                          SecurityToken = validatedToken
                      };
  
                      await Events.TokenValidated(tokenValidatedContext);
                      if (tokenValidatedContext.Result != null)
                      {
                          return tokenValidatedContext.Result;
                      }
  
                      if (Options.SaveToken)
                      {
                          tokenValidatedContext.Properties.StoreTokens(new[]
                          {
                              new AuthenticationToken { Name = "access_token", Value = token }
                          });
                      }
  
                      tokenValidatedContext.Success();
                      return tokenValidatedContext.Result;
                  }
              }
  
              if (validationFailures != null)
              {
                  var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
                  {
                      Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures)
                  };
  
                  await Events.AuthenticationFailed(authenticationFailedContext);
                  if (authenticationFailedContext.Result != null)
                  {
                      return authenticationFailedContext.Result;
                  }
  
                  return AuthenticateResult.Fail(authenticationFailedContext.Exception);
              }
  
              return AuthenticateResult.Fail("No SecurityTokenValidator available for token: " + token ?? "[null]");
          }
          catch (Exception ex)
          {
              ////Logger.ErrorProcessingMessage(ex);
  
              var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options)
              {
                  Exception = ex
              };
  
              await Events.AuthenticationFailed(authenticationFailedContext);
              if (authenticationFailedContext.Result != null)
              {
                  return authenticationFailedContext.Result;
              }
  
              throw;
          }
      }
  
      protected override async Task HandleChallengeAsync(AuthenticationProperties properties)
      {
          var authResult = await HandleAuthenticateOnceSafeAsync();
          var eventContext = new JwtBearerChallengeContext(Context, Scheme, Options, properties)
          {
              AuthenticateFailure = authResult?.Failure
          };
  
          // Avoid returning error=invalid_token if the error is not caused by an authentication failure (e.g missing token).
          if (Options.IncludeErrorDetails &amp;&amp; eventContext.AuthenticateFailure != null)
          {
              eventContext.Error = "invalid_token";
              eventContext.ErrorDescription = CreateErrorDescription(eventContext.AuthenticateFailure);
          }
  
          await Events.Challenge(eventContext);
          if (eventContext.Handled)
          {
              return;
          }
  
          Response.StatusCode = 401;
  
          if (string.IsNullOrEmpty(eventContext.Error) &amp;&amp;
              string.IsNullOrEmpty(eventContext.ErrorDescription) &amp;&amp;
              string.IsNullOrEmpty(eventContext.ErrorUri))
          {
              Response.Headers.Append(HeaderNames.WWWAuthenticate, Options.Challenge);
          }
          else
          {
              // https://tools.ietf.org/html/rfc6750#section-3.1
              // WWW-Authenticate: Bearer realm="example", error="invalid_token", error_description="The access token expired"
              var builder = new StringBuilder(Options.Challenge);
              if (Options.Challenge.IndexOf(" ", StringComparison.Ordinal) &gt; 0)
              {
                  // Only add a comma after the first param, if any
                  builder.Append(',');
                  builder.Append(',');
              }
              if (!string.IsNullOrEmpty(eventContext.Error))
              {
                  builder.Append(" error=\"");
                  builder.Append(eventContext.Error);
                  builder.Append("\"");
              }
              if (!string.IsNullOrEmpty(eventContext.ErrorDescription))
              {
                  if (!string.IsNullOrEmpty(eventContext.Error))
                  {
                      builder.Append(",");
                  }
  
                  builder.Append(" error_description=\"");
                  builder.Append(eventContext.ErrorDescription);
                  builder.Append('\"');
              }
              if (!string.IsNullOrEmpty(eventContext.ErrorUri))
              {
                  if (!string.IsNullOrEmpty(eventContext.Error) ||
                      !string.IsNullOrEmpty(eventContext.ErrorDescription))
                  {
                      builder.Append(",");
                  }
  
                  builder.Append(" error_uri=\"");
                  builder.Append(eventContext.ErrorUri);
                  builder.Append('\"');
              }
  
              Response.Headers.Append(HeaderNames.WWWAuthenticate, builder.ToString());
          }
      }
  
      private static string CreateErrorDescription(Exception authFailure)
      {
          IEnumerable&lt;Exception&gt; exceptions;
          if (authFailure is AggregateException agEx)
          {
              exceptions = agEx.InnerExceptions;
          }
          else
          {
              exceptions = new[] { authFailure };
          }
  
          var messages = new List&lt;string&gt;();
  
          foreach (var ex in exceptions)
          {
              // Order sensitive, some of these exceptions derive from others
              // and we want to display the most specific message possible.
              switch (ex)
              {
                  case SecurityTokenInvalidAudienceException _:
                      messages.Add("The audience is invalid");
                      break;
                  case SecurityTokenInvalidIssuerException _:
                      messages.Add("The issuer is invalid");
                      break;
                  case SecurityTokenNoExpirationException _:
                      messages.Add("The token has no expiration");
                      break;
                  case SecurityTokenInvalidLifetimeException _:
                      messages.Add("The token lifetime is invalid");
                      break;
                  case SecurityTokenNotYetValidException _:
                      messages.Add("The token is not valid yet");
                      break;
                  case SecurityTokenExpiredException _:
                      messages.Add("The token is expired");
                      break;
                  case SecurityTokenSignatureKeyNotFoundException _:
                      messages.Add("The signature key was not found");
                      break;
                  case SecurityTokenInvalidSignatureException _:
                      messages.Add("The signature is invalid");
                      break;
              }
          }
  
          return string.Join("; ", messages);
      }
  }
  </code></pre>
  
  <p>And then the Startup.cs to hook it up:</p>
  
  <pre><code>public class Startup
  {
      /// &lt;summary&gt;
      /// Initializes a new instance of the &lt;see cref="Startup"/&gt; class.
      /// &lt;/summary&gt;
      /// &lt;param name="configuration"&gt;The configuration.&lt;/param&gt;
      public Startup(IConfiguration configuration)
      {
          this.Configuration = configuration;
          ConfigureLogging();
      }
  
      /// &lt;summary&gt;
      /// Gets the configuration.
      /// &lt;/summary&gt;
      /// &lt;value&gt;
      /// The configuration.
      /// &lt;/value&gt;
      public IConfiguration Configuration { get; }
  
      /// &lt;summary&gt;
      /// Gets or sets the Container
      /// &lt;/summary&gt;
      private IUnityContainer Container { get; set; }
  
      public static void Main(string[] args)
      {
          BuildWebHost(args).Run();
      }
  
      public static IWebHost BuildWebHost(string[] args) =&gt;
          WebHost.CreateDefaultBuilder(args)
              .UseStartup&lt;Startup&gt;()
              .Build();
  
      public IServiceProvider ConfigureServices(IServiceCollection services)
      {
          var logger = Logger.For(this).ForAction(nameof(ConfigureServices));
  
          services.Configure&lt;GzipCompressionProviderOptions&gt;(options =&gt; options.Level = CompressionLevel.Optimal);
          services.AddResponseCompression();
  
          logger.Info("Configuring JWT Bearer Token Authorization...");
          services.AddAuthentication(options =&gt;
                  {
                      // the scheme name has to match the value we're going to use in AuthenticationBuilder.AddScheme(...)
                      options.DefaultAuthenticateScheme = "CustomScheme";
                      options.DefaultChallengeScheme = "CustomScheme";
                  })
              .AddCustomAuth(options =&gt; {
                      options.Audience = this.Configuration.ObtainConfiguredString(ConfigurationKeys.ValidAudienceId);
                      options.Authority = this.Configuration.ObtainConfiguredString(ConfigurationKeys.IssuerId);
                      options.SaveToken = false;
                      options.TokenValidationParameters = new TokenValidationParameters().WithConfiguredParameters(this.Configuration);
                  });
  
          logger.Info("Adding Authorization policies to Services...");
          services.AddAuthorization(
              options =&gt;
                  {
                      options.DefaultPolicy = new AuthorizationPolicyBuilder("CustomScheme").RequireAuthenticatedUser().Build();                        
                  });
  
          services.AddTransient&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
          services.AddTransient&lt;IAuthenticationHandler, CustomAuthHandler&gt;();
  
          EnableCors(services);
  
          logger.Info("Adding MVC support to Services...");
          services.AddMvc(config =&gt;
              {
                  var defaultPolicy = new AuthorizationPolicyBuilder(new[] { "CustomScheme" })
                      .RequireAuthenticatedUser()
                      .Build();
                  config.Filters.Add(new AuthorizeFilter(defaultPolicy));
              });
  
          Container = new UnityContainer();
  
          logger.Info("Registering other Services with UnityContainer...");
          Container.RegisterServices(Configuration);
  
          // Configure Microsoft DI for Unity resolution
          logger.Info("Configuring ASP.Net Core service resolution to use UnityContainer...");
          return services.UseUnityResolution(Container, s =&gt; s.BuildServiceProvider());
      }
  
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      /// &lt;summary&gt;
      /// The Configure
      /// &lt;/summary&gt;
      /// &lt;param name="app"&gt;The app&lt;see cref="IApplicationBuilder"/&gt;&lt;/param&gt;
      /// &lt;param name="env"&gt;The env&lt;see cref="IHostingEnvironment"/&gt;&lt;/param&gt;
      /// &lt;param name="loggerFactory"&gt;The loggerFactory&lt;see cref="ILoggerFactory"/&gt;&lt;/param&gt;
      /// &lt;param name="memoryCache"&gt;The memoryCache&lt;see cref="IMemoryCache"/&gt;&lt;/param&gt;
      /// &lt;param name="contextAccessor"&gt;The contextAccessor&lt;see cref="IHttpContextAccessor"/&gt;&lt;/param&gt;
      /// &lt;param name="authzClient"&gt;The authzClient&lt;see cref="IAuthzClient"/&gt;&lt;/param&gt;
      public void Configure(
          IApplicationBuilder app,
          IHostingEnvironment env,
          ILoggerFactory loggerFactory,
          IMemoryCache memoryCache,
          IHttpContextAccessor contextAccessor,
          IAuthzClient authzClient)
      {
          var logger = Logger.For(this).ForAction(nameof(Configure));
  
          logger.Info("Configuring ASP.Net Core logging framework...");
  
          loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
          loggerFactory.AddDebug();
  
          var corsEnabled = this.Configuration.ObtainConfiguredBooleanWithDefault(ConfigurationKeys.EnableCors, false);
          if (corsEnabled)
          {
              app.UseCors("CorsPolicy");
          }
  
          logger.Info("Configuring ASP.Net Core custom status page...");
          app.UseStatusCodePagesWithReExecute("/error/{0}");
  
          if (env.IsDevelopment())
          {
              logger.Info("Configuring development middle-ware...");
              app.UseDeveloperExceptionPage();
              app.UseBrowserLink();
          }
  
          logger.Info("Configuring standard ASP.Net Core behaviors...");
          app.UseDefaultFiles();
          app.UseStaticFiles();
  
          ////app.UseAuthentication();
          app.Use(async (context, next) =&gt;
              {
                  if (!context.User.Identity.IsAuthenticated)
                  {
                      var result = await context.AuthenticateAsync("CustomScheme");
                      if (result?.Principal != null)
                      {
                          context.User = result.Principal;
                      }
                  }
  
                  await next.Invoke();
              });
  
          app.UseMvc();
          app.WithRequestLogging();          
      }
  
      private void EnableCors(IServiceCollection service)
      {
          var logger = Logger.For(this).ForAction(nameof(EnableCors));
          var corsEnabled = this.Configuration.ObtainConfiguredBooleanWithDefault(ConfigurationKeys.EnableCors, false);
  
          if (corsEnabled)
          {
              logger.Verbose("Configuring ASP.Net Core CORS support...");
  
              service.AddCors(
                  options =&gt;
                      {
                          options.AddPolicy("CorsPolicy",
                              builder =&gt;
                                  {
                                      builder.AllowAnyOrigin();
                                      builder.AllowAnyHeader();
                                      builder.AllowAnyMethod();
                                      builder.AllowCredentials();
                                  });
                      });
          }
      }
      }
  }
  </code></pre>
  
  <p>Can someone please tell me what I am doing wrong? First time around when I fire the postman request with the correct AuthorizationHeader with the access token this line executes the CustomAuthHandler:</p>
  
  <pre><code>var result = await context.AuthenticateAsync("CustomScheme");
  </code></pre>
  
  <p>However the second time around the debugger steps over that code? This is driving me up the wall. I must be missing something basic!</p>
  
  <p><strong>EDIT:</strong>
  In the Core 1.x version, ConfigureServices was setup as</p>
  
  <pre><code>public IServiceProvider ConfigureServices(IServiceCollection services)
      {
          var logger = Logger.For(this).ForAction(nameof(ConfigureServices));
  
          logger.Verbose("Adding MVC support to Services...");
          // Add framework services.
          services.AddMvc();
  
          logger.Verbose("Adding Authorization policies to Services...");
          services.AddAuthorization(
              options =&gt;
              {
                  options.AddPolicy(
                      "SomePermission",
                      policy =&gt; policy.RequireClaim("claimUrl", "Some Permission"));
              });
  
          services.AddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
  
          container = new UnityContainer();
  
          logger.Verbose("Registering other Services with UnityContainer...");
          container.RegisterServices(Configuration);
  
          // Configure Microsoft DI for Unity resolution
          logger.Verbose("Configuring ASP.Net Core service resolution to use UnityContainer...");
          return services.UseUnityResolution(container, s =&gt; s.BuildServiceProvider());
      }
  </code></pre>
  
  <p>And Configure() was wired up as follows</p>
  
  <pre><code> app.UseAuth0JwtBearerAuthentication(
              new JwtBearerOptions
              {
                  AutomaticAuthenticate = true,
                  AutomaticChallenge = true,
                  TokenValidationParameters =
                          new TokenValidationParameters().WithConfiguredParameters(this.Configuration)
              });
  
  if (env.IsDevelopment())
          {
              logger.Verbose("Configuring development middleware...");
              app.UseDeveloperExceptionPage();
              app.UseBrowserLink();
          }
  
          logger.Verbose("Configuring standard ASP.Net Core behaviors...");
          app.UseDefaultFiles();
          app.UseMvc();
          app.UseStaticFiles();
  </code></pre>
  
  <p>Using this version if I execute postman calls, then I get a new ClaimsPrincipal for every request. So what has changed in ASP.NET Core 2.1?</p>