0

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!

serlingpa
  • 12,024
  • 24
  • 80
  • 130
  • Over HTTP, JWT is sent in headers. This is not normally available in SignalR. Instead, [you should send it as query string parameter](https://learn.microsoft.com/en-us/aspnet/core/signalr/authn-and-authz?view=aspnetcore-3.1). – Riwen Sep 28 '20 at 19:11
  • I have implemented that already – serlingpa Sep 28 '20 at 19:18
  • I am not having a problem authenticating, I am having a problem getting the sub, – serlingpa Sep 28 '20 at 19:42
  • Okay, let me understand. You can [decode the JWT](https://stackoverflow.com/questions/38340078/how-to-decode-jwt-token) anywhere. Does that solve the issue? – Riwen Sep 28 '20 at 19:48
  • How do I get the decoded JWT within my `ChatHub` class? – serlingpa Sep 28 '20 at 19:59
  • I have tried passing in an `IHubContext` to `ConfigServices()`, thinking the DI would handle it, but the framework complained... – serlingpa Sep 28 '20 at 20:32
  • It is generally not recommended to directly get the HttpContext from a hub, but you can [do it](https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.signalr.gethttpcontextextensions.gethttpcontext?view=aspnetcore-3.1). From there, you can get the query string parameter, and decode the JWT. However, since the user is already authenticated, you should not rely on this at all. Rather, use the `Context.User` property to get the currently authenticated user. – Riwen Sep 28 '20 at 21:02
  • Yeah but that `Context.User` doesn't appear to have any useful information in it! Can I decode the JWT in the `OnMessageReceived()` and push it into the user object somehow? – serlingpa Sep 28 '20 at 21:40
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/222206/discussion-between-riwen-and-serlingpa). – Riwen Sep 28 '20 at 21:59

0 Answers0