Blazor WebAssembly SignalR 身份验证在协商时未通过 AccessToken

Blazor WebAssembly SignalR authentication not passing AccessToken upon negotiation

我正在尝试使用 SignalR 为 Blazor WASM 应用程序实施身份验证例程,并且 运行 基本上是在墙上。

我已经启动了一个外部 Keycloak 服务器并且 运行ning 并且 WASM 应用程序成功地通过了那个服务器的身份验证;客户端实际上获得了一个有效的 JWT 令牌和所有。当我尝试让 SignalR Hub 和客户端验证我 运行 遇到问题时。不过,只要我不将 [Authenticate] 添加到集线器,就会建立连接。

根据官方文档,这就是我应该让客户端连接到集线器的方式:

hubConnection = new HubConnectionBuilder()
                .WithUrl(NavigationManager.ToAbsoluteUri("/chathub"), options =>
                {
                    options.AccessTokenProvider = () => Task.FromResult(_accessToken);
                })
                .Build();

在 SignalR Hub 上我应该这样做:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
    .AddJwtBearer(options =>
{
    options.Authority = "https://keycloak/auth/realms/master/";
    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;
        }
    };
});

我在客户端得到的只是控制台上的一个错误,带有一个大 401(即“未授权”) 我能够向应用程序添加自定义授权例程(每次身份验证尝试都返回“成功”)并找出问题的可能根源:

客户端对集线器进行 两次 连接尝试。第一个是/chathub/negotiate?negotiateVersion=1,第二个是/chathub.

但是,只有第二个请求携带了access_token!结果,使用上面的代码将在第一步中断,因为 access_token 似乎在谈判阶段已经需要 HubConnectionBuilder 由于某种原因不提供该参数。

我做错了什么?

编辑:请参阅下面的答案。问题不是缺少令牌,而是缺少 options.Audience 设置。

好的,我终于找到了问题和解决方案。我对令牌验证无声地失败这一事实感到恼火,然后仔细研究了处理令牌的中间件。 我注意到它基本上覆盖了一个事件处理程序并问自己是否有 other 个事件处理程序?

好吧,你瞧,像这样添加 OnAuthenticationFailed 并在 return 上设置断点让我看到了实际的错误消息:

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;
                },
                OnAuthenticationFailed = context =>
                {
                    context.Response.StatusCode = 401;
                    return Task.CompletedTask;
                }
            };

表示 Audience 属性 为空。我现在所要做的就是将正确的映射添加到我的 Keycloak 服务器(请参阅此 线程)并将 options.Audience = "ClientId" 添加到配置中,如下所示:

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
        .AddJwtBearer(options =>
        {
            options.Authority = "https://keycloak/auth/realms/master";
            options.Audience = "ClientID";
            options.Events = new JwtBearerEvents
            {
[...]