I have a REST API (core 2.1) which need to support a SPA, providing restful endpoints and some live-interactive features using SignalR;
The Hub/MVC routes are running on the same server, which is also the provider of the JWT token.
After loggin in, the client-side receives a JWT token, which places on the header for every REST request, otherwise it gets a 401 (This is working with the [Authorize] attribute).
At the client-side, the code below tries to connect to my /hub endpoint: 
new HubConnectionBuilder().withUrl(HUB_URL, { accessTokenFactory: () => this.getToken() })
And if I place [Authorize] at my Hub class, I get the following error (Without the authorization, the client can send and listen correctly):
WebSocket connection to 'wss://localhost:5001/hub?id=MY_ID&access_token=MY_TOKEN' failed: HTTP Authentication failed; no valid credentials available
The server logged failed authentications:
(Triggered a console.log on AddJwtBearerOptions.Events.OnMessageReceived)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET https://localhost:5001/hub? id=MY_ID&access_token=MY_TOKEN
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
Request finished in 0.3658ms 401
Differently from the those requests, using the SAME JWT TOKEN with the REST ones (Using [Authorize]), with the Header: Bearer XXXX instead of  querystring, triggers the OnTokenValidated. The OnAuthenticationFailed is never triggered, even if the authentication fails:
(Triggered a console.log on AddJwtBearerOptions.Events.OnMessageReceived)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
Request starting HTTP/1.1 GET https://localhost:5001/api/products application/json
(Triggered a console.log on AddJwtBearerOptions.Events.OnTokenValidated)
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[2]
Successfully validated the token.
ConfigureServices(IServiceCollection)
services.AddSignalR();
services.AddCors(option => option.AddPolicy("CorsPolicy", p => p.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod().AllowCredentials()));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
    options.TokenValidationParameters = new TokenValidationParameters{
        ValidateIssuer = true,
        ValidIssuer = Configuration["JWT:Issuer"],
        ValidateLifetime = true,
        ValidateAudience = false,
        ValidateIssuerSigningKey = true,
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JWT:SecurityKey"]))
    };
});
Configure(IApplicationBuilder)
app.UseAuthentication();
app.UseCors("CorsPolicy");
app.UseHttpsRedirection();
app.UseMvc();
app.UseSignalR(routes => {
     routes.MapHub<ApplicationHub>("/hub");
});
                The SignalR JS client gets the JWT via a factory method on the HubConnectionBuilder. An interesting thing is that SignalR doesn't appear to support the Bearer [My Token] Authorization header.
On the server, bearer token authentication is configured using the JWT Bearer middleware:
Token cannot be revoked once created; token can only expire. It should be a guide only! There needs to be work put in to make it production ready. When logged on with Firebase Authentication, the client is provided a Json Web Token (JWT).
As there are no backchannel requests, no API limits can be hit. Token cannot be revoked once created; token can only expire. It should be a guide only! There needs to be work put in to make it production ready. When logged on with Firebase Authentication, the client is provided a Json Web Token (JWT).
You also need to add this block inside the .AddJwtBearer section:
// We have to hook the OnMessageReceived event in order to
// allow the JWT authentication handler to read the access
// token from the query string when a WebSocket or 
// Server-Sent Events request comes in.
options.Events = new JwtBearerEvents
{
    OnMessageReceived = context =>
    {
        var accessToken = context.Request.Query["access_token"];
        // If the request is for our hub...
        var path = context.HttpContext.Request.Path;
        if (!string.IsNullOrEmpty(accessToken) &&
            (path.StartsWithSegments("/hub")))
        {
            // Read the token out of the query string
            context.Token = accessToken;
        }
        return Task.CompletedTask;
    }
};
This can be found here in the docs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With