title: JWT + SignalR on ASP Core 3 resulting in 401 Unauthorized
body: <p>If I use http calls outside of signalr, such as with postman or httpclient, I am able to have my token validated successfully on the server. It's when I try to connect through my signalr hub that the token is not passing authorization.</p>
  
  <blockquote>
    <p>Bearer was not authenticated. Failure message: No SecurityTokenValidator available for token: Bearer MyTokenFooBar</p>
  </blockquote>
  
  <p>My service setup is:</p>
  
  <pre><code>public void ConfigureServices(IServiceCollection services)
  {
      services.AddRouting();
      services.AddControllers();
      services.AddHealthChecks();
      services.AddDbContext&lt;ApplicationDbContext&gt;(options =&gt; options.UseSqlServer(builder =&gt; { builder.ConnectionString = _configuration.GetConnectionString("DefaultConnection"); }));
      services.AddIdentity&lt;ApplicationUser, IdentityRole&gt;(setup =&gt;
      {
          // foo
      }).AddEntityFrameworkStores&lt;ApplicationDbContext&gt;().AddDefaultTokenProviders();
  
      services.AddAuthentication(options =&gt;
          {
              options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
              options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
          })
          .AddJwtBearer(options =&gt;
          {
              options.RequireHttpsMetadata = false;
              options.SaveToken = true;
              options.TokenValidationParameters = new TokenValidationParameters
              {
                  ValidIssuer = _configuration["Jwt:Issuer"],
                  ValidAudience = _configuration["Jwt:Audience"],
                  ValidateIssuer = false,
                  ValidateAudience = false,
                  ValidateIssuerSigningKey = false,
                  IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])),
                  ValidateLifetime = false
              };
  
              options.Events = new JwtBearerEvents
              {
                  OnMessageReceived = context =&gt;
                  {
                      var path = context.HttpContext.Request.Path;
                      if (!path.StartsWithSegments("/chat")) return Task.CompletedTask;
                      var accessToken = context.Request.Headers[HeaderNames.Authorization];
                      if (!string.IsNullOrWhiteSpace(accessToken) &amp;&amp; context.Scheme.Name == JwtBearerDefaults.AuthenticationScheme)
                      {
                          context.Token = accessToken;
                      }
  
                      return Task.CompletedTask;
                  }
              };
          });
  
      services.AddAuthorization();
  
      services.AddSignalR(options =&gt; { options.EnableDetailedErrors = true; });
  }
  
  public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
  {
      app.UseRouting();
      app.UseAuthentication();
      app.UseAuthorization();
      app.UseEndpoints(options =&gt;
      {
          options.MapHealthChecks("/health");
          options.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
      });
      app.UseSignalR(options =&gt; { options.MapHub&lt;ChatHub&gt;("/chat"); });
  }
  </code></pre>
  
  <p>I use a basic http auth header for the initial connection, which will sign the user into identity and generate a jwt token as a response for use in future calls.</p>
  
  <pre><code>[HttpPost]
  [AllowAnonymous]
  public async Task&lt;IActionResult&gt; Login()
  {
      var (headerUserName, headerPassword) = GetAuthLoginInformation(HttpContext);
  
      var signInResult = await _signInManager.PasswordSignInAsync(headerUserName, headerPassword, false, false);
      if (!signInResult.Succeeded)
      {
          return Unauthorized();
      }
  
      var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("SuperTopSecretKeyThatYouDoNotGiveOutEver!"));
      var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256);
      var jwt = new JwtSecurityToken(signingCredentials: signingCredentials);
      var handler = new JwtSecurityTokenHandler();
      var token = handler.WriteToken(jwt);
      return new OkObjectResult(token);
  }
  </code></pre>
  
  <p>And my client (a console application) is setup to cache this token and use it in future signalr calls as such:</p>
  
  <p>Get the token:</p>
  
  <pre><code>_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(encoding.GetBytes($"{userName}:{password}")));
  var response = await _client.SendAsync(request); // this goes to the login action posted above
  _token = await response.Content.ReadAsStringAsync();
  </code></pre>
  
  <p>...</p>
  
  <pre><code>_hubConnection = new HubConnectionBuilder()
      .WithUrl(new Uri(_baseAddress, "chat"),
          options =&gt; { options.AccessTokenProvider = () =&gt; Task.FromResult(_token); }) // send the cached token back with every request
      .Build();
  
  // here is where the error occurs. 401 unauthorized comes back from this call.
  await _hubConnection.StartAsync();
  </code></pre>