2
votes

I have a major issue I can not solve. I have a.net core 2 project with jwt auth and trying to get a basic signalr hub test to work. The connection from the client seems to work but the connection is promptly dropped with a 204 code.

My hub is running on another URL than my front end UI so CORS is involved.

**I removed real keys and my project name

this is what the client console outputs:

console

Here is my startup code:

public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.Configure<ConnectionStrings>(Configuration.GetSection("ConnectionStrings"));
        services.Configure<DiAssemblies>(Configuration.GetSection("DiAssemblies"));
        services.Configure<StripeSettings>(Configuration.GetSection("Stripe"));
        services.Configure<AzureStorageSettings>(Configuration.GetSection("AzureStorageSettings"));

        // Add framework services.
        services.AddApplicationInsightsTelemetry(Configuration);
        services.AddCors(options =>
        {
            options.AddPolicy("CorsPolicy",
                x => x.AllowAnyOrigin()
                    .AllowAnyMethod()
                    .AllowAnyHeader()
                    .AllowCredentials());
        });


        services.AddCors();

        services.AddIdentity<testApp.Identity.ApplicationUser, IdentityRole>()
        .AddEntityFrameworkStores<testApp.Identity.Context>()
        .AddDefaultTokenProviders();

        services.AddAuthentication(sharedOptions =>
        {
            sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;


        })
            .AddJwtBearer(options =>
            {
                options.RequireHttpsMetadata = false;
                options.SaveToken = true;

                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
                {
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("authkeyasdfasdfasdfsadfasdfda")),
                    ValidIssuer = "https://testApp.com/",
                    ValidAudience = "https://testApp.com/",

                };
                options.Events = new JwtBearerEvents
                {

                    OnAuthenticationFailed = context =>
                    {
                        Debug.WriteLine("Auth issue");
                        Debug.WriteLine(context.Exception);
                        return Task.CompletedTask;
                    },

                    OnMessageReceived = context =>
                    {
                        if (!context.Request.Headers.ContainsKey("Authorization") && !string.IsNullOrEmpty(context.Request.Query["authToken"]))
                        {
                            context.Request.Headers.Add("Authorization", context.Request.Query["authToken"]);
                        }
                        return Task.CompletedTask;
                    },
                    OnChallenge = context =>
                    {
                        Debug.WriteLine("Auth issue");
                        Debug.WriteLine(context.Error);
                        Debug.WriteLine(context.ErrorDescription);
                        return Task.CompletedTask;
                    }
                };
            });

        services.AddMemoryCache();

        services.AddMvc().AddJsonOptions(options =>
        {
            options.SerializerSettings.ContractResolver =
                new CamelCasePropertyNamesContractResolver();
            options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });

        services.AddSockets();
        services.AddSignalRCore();

        var builder = new ContainerBuilder();

        // Register dependencies, populate the services from
        // the collection, and build the container. If you want
        // to dispose of the container at the end of the app,
        // be sure to keep a reference to it as a property or field.
        //builder.RegisterType<MyType>().As<IMyType>();
        builder.Populate(services);
        var diHelper = new DiHelper();
        var diAssemblies = Configuration.GetValue<string>("DiAssemblies:List");
        var assemblies = diHelper.GetAssemblies(diAssemblies.Split(',').ToList());
      //  Console.Write(diAssemblies);
        builder.RegisterAssemblyModules(assemblies);

        ApplicationContainer = builder.Build();

        //foreach(var assembly in assemblies)
        //    services.AddMvc().AddApplicationPart(assembly).AddControllersAsServices();

        // Create the IServiceProvider based on the container.
        return new AutofacServiceProvider(ApplicationContainer);
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

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


        app.UseAuthentication();


        app.UseMvc();

        app.UseSignalR(routes =>
        {
            routes.MapHub<ChatHub>("chat");
        });
    }
}

My Hub (a break point on any of the methods never gets hit)

public class ChatHub : Hub
    {
        public ChatHub()
        {
            var t = 0;
        }


        public override async Task OnConnectedAsync()
        {
            await Clients.Client(Context.ConnectionId).InvokeAsync("send", "connection made");

            await base.OnConnectedAsync();
        }

        public Task Send(string message)
        { 
            return Clients.All.InvokeAsync("send", message); 
        }
    }

Client javascript:

<script>
      window.connection;
      window.connection = new signalR.HubConnection("https://localhost:44367/chat?authToken=" + 'bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.yyJzdWIiOiJhYzNkMTU3OC05YjU5LTRmNzQtOWMxYi01MWZlYjk2YmQ4YzEiLCJqdGkiOiJidnRlc3QxIiwiZXhwIjoxTYI2NjY1OTM3LCJpc3MiOiJodHRwczovL2Jpc3ZpbmUuY29tLyIsImF1ZCI6Imh0dHBzOi1vYmlzdmluZS5jb20vIn0.GxycqmyVsdHkW3M5yRH7arGkR3K-jAE2zrPrgoJnh-M', 
                                { transport: signalR.TransportType.LongPolling });

      window.connection.on('send', function(message) {
        alert(message);
      });


      window.connection.start().then(function() {
        console.log("SignalR Connected: Chat");
        window.connection.invoke('send',"hi").then(function (chats) {
          console.log(chats);
        });
      }, function () {

      });
    </script>
1
can you remove the transport: signalR.TransportType.LongPollingjohnny 5
No signalr 2 requires you to set a transportracamp101
Signalr 2 or .net core 2. Why are you using long polling by default instead of using web sockets?johnny 5
testing on windows 7racamp101
Hmm lemme check my project for differences off the top of my head. Where is your authentication middleware to intercept your get parameter and translate it to jwt?johnny 5

1 Answers

1
votes

I knew this had to be something easy but hopefully if anyone else runs into this it'll help them too.

After pulling my hair out I took a step back and noticed the logs were showing the hub was found I noticed hidden a few lines up that another mvc controller was also being invoked. After finding the controller I noticed it did not have the attribute [Route("api/[controller]")].

Added that and boom everything started working! Seems that every controller needs to have at least a route set or else mvc will take the call before signalr has a chance to fully fire.