Authentication error when SignalR tries to negotiate on HubConnection.StartAsync

Viewed 155

I have a blazor server side web app that uses AAD for authentication. I have added SignalR to it according to the guidance. It starts up fine, but when I try to connect using the following code:

var hubUrl = _navigationManager.ToAbsoluteUri("messaging");
hubConnection = new HubConnectionBuilder()
   .WithUrl(hubUrl)
   .Build();
await hubConnection.StartAsync();
  

I get an exception. This the console debug trace:

[INF] Request finished HTTP/2 GET https://localhost:5001/css/insight-icons/fonts/Insight.woff2 - - - 304 - font/woff2 3.1879ms
[INF] Request starting HTTP/1.1 POST https://localhost:5001/messaging/negotiate?negotiateVersion=1 - 0
[INF] OpenIdConnect was not authenticated. Failure message: Not authenticated
[INF] Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
[INF] AuthenticationScheme: OpenIdConnect was challenged.
[INF] Request finished HTTP/1.1 POST https://localhost:5001/messaging/negotiate?negotiateVersion=1 - 0 - 302 0 - 962.8463ms

As mentioned I am logged in and authenticated using AAD.

Any suggestions as to what I am doing wrong of what is missing?

1 Answers

Note the token config:

hubConnection = new HubConnectionBuilder()
    .WithUrl(hubUrl), options =>
    {
        options.AccessTokenProvider = async () =>
        {
            var accessTokenResult = await AccessTokenProvider.RequestAccessToken();
            accessTokenResult.TryGetToken(out var accessToken);
            var token = accessToken.Value;
            return token;
        };
    })
    .Build();

You should also configure the middleware one the server to add the tokens to requests going to the hub. (Not required to start the connection)

services.TryAddEnumerable(
  ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,ConfigureJwtBearerOptions>());

ConfigureJwtBearerOptions.cs (Change the endpoint)

public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{
    public void PostConfigure(string name, JwtBearerOptions options)
    {
        var originalOnMessageReceived = options.Events.OnMessageReceived;
        options.Events.OnMessageReceived = async context =>
        {
            await originalOnMessageReceived(context);

            if (string.IsNullOrEmpty(context.Token))
            {
                var accessToken = context.Request.Query["access_token"];
                var requestPath = context.HttpContext.Request.Path;
                var endPoint = $"/chathub";

                if (!string.IsNullOrEmpty(accessToken) &&
                    requestPath.StartsWithSegments(endPoint))
                {
                    context.Token = accessToken;
                }
            }
        };
    }
}
Related