4

I know there are a lot of questions and answeres regarding this topic out there, but nothing matched my specific issue.

I am using the following versions

  • Angular 10.0.14
    • @aspnet/signalr 1.0.27
  • ASP.NET Core 3.1

VERSION UPDATE:

  • I just replaced @aspnet/signalr 1.0.27 by @microsoft/signalr 5.0.11 -> same issue.

The SignalR connection works pretty fine until I add an accessTokenFactory in the Angular frontend.

Frontend

this.hubConnection = new signalR.HubConnectionBuilder()
  .withUrl(`${environment.bzApiBaseUrl}/hubs/heartbeat`, {
    accessTokenFactory: () => token
  })
  .build();

this.hubConnection
  .start()
  .then(() => {
    console.log('SignalR: Heartbeat - connection started');

    this.hubConnection.on('beat', () => {
      console.log('SignalR: Heartbeat - Heartbeat received');
    });
  })
  .catch(err => console.log('SignalR: Heartbeat - error while starting connection: ' + err));
  });

The connection gets established when I remove the accessTokenFactory from HubConnectionBuilder.

Backend

services.AddCors(options =>
{
    options.AddPolicy(
        name: MyAllowSpecificOrigins,
        builder =>
        {
            builder
                .WithOrigins(CorsSettings.TargetDomain)
                .AllowAnyHeader()
                .AllowAnyMethod()
                .SetIsOriginAllowed((host) => true)
                .AllowCredentials();
        });
});

The value of the CorsSetting domain is http://localhost:4200 where the frontend is running.

app.UseStaticFiles();
app.UseRouting();
app.UseCors(MyAllowSpecificOrigins);
app.UseAuthentication();
app.UseAuthorization();

app.UseEndpoints(e =>
{
    e.MapControllers();
    e.MapHub<HeartbeatHub>("/api/hubs/heartbeat");
});

The following error gets logged in browsers console after adding accessTokenFactory:

WebSocketTransport.js:70 
WebSocket connection to 'ws://localhost:33258/api/hubs/heartbeat?id=BpBytwkEatklNR-XqGtabA&access_token=eyJ0eXAiOiJKV1QiLCJhbGci... failed:

Utils.js:190 
Error: Failed to start the transport 'WebSockets': undefined

dashboard:1 
Access to resource at 'http://localhost:33258/api/hubs/heartbeat?id=iWuMeeOKkWCUa8X9z7jXyA&access_token=eyJ0eXAiOiJKV1QiLCJhbGci...' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I don't understand the CORS issue due to the fact that it seems to be raised by adding a token to the query string with the accessTokenFactory.

I tried:

  • Adding the backend URL to CORS origin (not needed according to documentation)
  • app.UseSignalR instead of app.UseEndpoints
  • Disable app.UseHttpsRedirection()
  • Changed the order of the registered middlewares in different ways
  • .SetIsOriginAllowed((host) => true) after .AllowCredentials()

UPDATE

The connection gets established when setting the HttpTransportType to LongPolling. It does not work with WebSockets or ServerSentEvents.

app.UseEndpoints(e =>
{
    e.MapControllers();
    e.MapHub<HeartbeatHub>("/api/hubs/heartbeat", options =>
    {
        options.Transports = HttpTransportType.LongPolling;
    });
});

It seems as the token gets send as an HTTP header instead of a query string parameter when using long polling. The token is very long, so I break through the max. allowed URL length when using WebSockets or ServerSentEvents. But I have still no idea why this leads to a CORS exception.

Sebastian S.
  • 1,173
  • 3
  • 13
  • 22
  • 2
    Please try `@microsoft/signalr` because `@aspnet\signlar` is deprecated. – Fabian Oct 19 '21 at 09:19
  • @Fabian I replaced the package without any effect, still the same error. – Sebastian S. Oct 19 '21 at 09:21
  • The order is important: https://stackoverflow.com/questions/59468525/access-to-xmlhttprequest-has-been-blocked-origin-asp-net-core-2-2-0-angular-8/59487002#59487002 – Kiril1512 Oct 19 '21 at 10:19
  • 1
    @Kiril1512 Thanks for the hint, but still the same error after changing the order. – Sebastian S. Oct 19 '21 at 10:21
  • @SebastianS. yes but what about your "Use"? You should try place "UseAuthentication" and "UseAuthorization" before the "UseCors" and others like "UseRouting". – Kiril1512 Oct 19 '21 at 10:28
  • @Kiril1512 according to the Microsoft docs the current order should be right: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1 – Sebastian S. Oct 19 '21 at 10:33
  • Maybe the problem is how you map the signalr hub? As you can see in this example: https://github.com/Kiril1512/SignalRDemo/blob/master/Server/Startup.cs#L57 you just need to add ``/heartbeat to the mapper and on the client side just call `.withUrl`(http://localhost:33258/heartbeat`. – Kiril1512 Oct 19 '21 at 11:31

1 Answers1

3

Browsers do not support headers for websockets, therefore the bearer token has to be added as query string parameter. We hit the maximum length for URLs due to the length of our bearer token. We could shorten our token or use a reference token, see also: https://github.com/aspnet/SignalR/issues/1266

Hope this helps others as well.

Sebastian S.
  • 1,173
  • 3
  • 13
  • 22