Sebastian S.
Sebastian S.

Reputation: 1391

SignalR CORS issue with Angular and .NET Core

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

VERSION UPDATE:

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:

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.

Upvotes: 4

Views: 1141

Answers (1)

Sebastian S.
Sebastian S.

Reputation: 1391

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.

Upvotes: 3

Related Questions