I had developed a chat app that uses socket.io for communication between client and server, but I ran into serious problems when trying to scale across more than one node. So I decided to give SignalR a go...
I am using Auth0 for my authentication and had previously developed a library to allow Auth0 to integrate with socket.io. I am now trying to understand how I can integrate Auth0 with SignalR and my React front end.
It is going well, I have managed to get the app authenticating over the socket/HTTP handshake, but I am struggling to understand how to obtain the sub and other information from the decoded JWT, since this sub uniquely identifies my user to the application in general, but to the SignalR hubs in particular.
The difficulty is that the authentication is happening over HTTP, and so the hub is unavailable at that point (or is it? Told you I didn't know what I was talking about...)
I am completely new to ASP.NET Core, and SignalR, so I think I've done quite well! But can someone help me out with obtaining the JWT within my hubs? Here is my startup.cs
:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder
.WithOrigins("http://localhost:3010", "http://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
var domain = $"https://{Configuration["Auth0:Domain"]}/";
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = domain;
options.Audience = Configuration["Auth0:Audience"];
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("/chathub")))
{
// Read the token out of the query string
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("read:messages",
policy => policy.Requirements.Add(new HasScopeRequirement("read:messages", domain)));
});
services.AddControllers();
services.AddSignalR();
// Register the scope authorization handler
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors("AllowSpecificOrigin");
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<ChatHub>("/chathub");
});
}
}
and here is my hub, such as it is:
namespace WebAPIApplication.Hubs
{
[Authorize]
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
// How do I access the JWT sub here?
await Clients.All.SendAsync("ReceiveMessage", "Some message");
}
}
}
Please help!