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.