Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JWT Authentication Failed with SignalR

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.


Check below my ´Startup.cs`

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");
});
like image 706
Bruno Miquelin Avatar asked Oct 13 '18 20:10

Bruno Miquelin


People also ask

How to get JWT token from SignalR?

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.

How is JWT bearer authentication configured on the server?

On the server, bearer token authentication is configured using the JWT Bearer middleware:

Is it possible to revoke a JWT token?

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).

Is there an API limit for JWT token in Firebase?

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).


Video Answer


1 Answers

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.

like image 71
marcusturewicz Avatar answered Sep 19 '22 07:09

marcusturewicz