Home > other >  how to set identity on server side for a client on blazor server with JWT
how to set identity on server side for a client on blazor server with JWT

Time:08-29

I have "correctly" obtained a working JWT authentication although that I cant get the server to recognize the Identity for the clien after logging in(with JWT or in case of using cookies when creating the HubConnectionBuilder

I have setup SignaLr to recognize the user by Id in this case Email/Name

the connection in case of using cookies it recognizes the client...until i create the HubConnection

the way i set the Id for the user for signalr

 public class CustomEmailProvider : IUserIdProvider
    {
        public virtual string GetUserId(HubConnectionContext connection)
        {
            
            var res = connection.User?.Claims.FirstOrDefault(x=>x.Type==ClaimTypes.NameIdentifier && x.Value == connection?.User?.Identity?.Name);
            return res?.Value;
        }
    }

in program cs using jwt

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme/*"Identity.Application"*/;
}).AddJwtBearer(options => { 
        options.TokenValidationParameters =new TokenValidationParameters()
        {
            ValidateAudience=true,
            ValidateIssuer=true,
            ValidateLifetime=true,
            ValidateIssuerSigningKey=true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidAudience=builder.Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"])),

        };
});

...

builder.Services.AddHttpClient();
builder.Services.AddSignalR();
builder.Services.AddSingleton<IUserIdProvider, CustomEmailProvider>();
builder.Services.AddResponseCompression(options => options.MimeTypes.Concat(new[] { "application/octet-stream" }));
builder.Services.AddCors(options=>options.AddDefaultPolicy(builder=>builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()));
...


app.UseRouting();
app.UseCors(options => options.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
app.UseAuthentication();

app.UseAuthorization();
app.UseEndpoints(endPoints => { endPoints.MapHub<ConnectionHub>("/ConnectionsHub");

using cookies

   .AddTokenProvider<DataProtectorTokenProvider<AppUser>>(TokenOptions.DefaultProvider);
builder.Services.AddAuthentication("Identity.Application")
    .AddCookie();
...
builder.Services.AddSignalR();
builder.Services.AddSingleton<IUserIdProvider, CustomEmailProvider>();
builder.Services.AddResponseCompression(options => options.MimeTypes.Concat(new[] { "application/octet-stream" }));
builder.Services.AddCors(options=>options.AddDefaultPolicy(builder=>builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()))
...
app.UseRouting();
app.UseCors(options => options.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
app.UseAuthentication();

app.UseAuthorization();
app.UseEndpoints(endPoints => { endPoints.MapHub<ConnectionHub>("/ConnectionsHub"); });

app.MapBlazorHub();

in the component


 protected override async Task OnInitializedAsync()
{
  if (hub is null)
                hub = new HubConnectionBuilder()
                    .WithUrl(_NavigationManager.ToAbsoluteUri("/ConnectionsHub"))
                    .Build();

}

so in the hub when i set the attribute [Authorize] given in the ID is not getting any Identity(after creating the hub in the case of the JWT it doesnt recognize it at any moment) I wonder how to correctly send the Identity claims to the server?

CodePudding user response:

When creating a hub connection you need to pass the access token.

new HubConnectionBuilder()
    .WithUrl(url, options =>
    {
      options.AccessTokenProvider = () => Task.FromResult("your access token");
    });

You also need to configure JwtBearerOptions with the following:

.AddJwtBearer(options =>
{
    // 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.

    // Sending the access token in the query string is required due to
    // a limitation in Browser APIs. We restrict it to only calls to the
    // SignalR hub in this code.
    // See https://docs.microsoft.com/aspnet/core/signalr/security#access-token-logging
    // for more information about security considerations when using
    // the query string to transmit the access token.
    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("/ConnectionsHub")))
            {
                // Read the token out of the query string
                context.Token = accessToken;
            }
            return Task.CompletedTask;
        }
    };
});

SignalR sends the access token on the query string, but the server expects the access token on the Bearer authentication header. So we have to read it manually.

https://docs.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-6.0#bearer-token-authentication

  • Related