【问题标题】:Blazor WebAssembly SignalR authentication not passing AccessToken upon negotiationBlazor WebAssembly SignalR 身份验证在协商时未通过 AccessToken
【发布时间】:2022-01-21 13:48:46
【问题描述】:

我正在尝试使用 SignalR 为 Blazor WASM 应用程序实现身份验证例程,但基本上是碰壁了。

我已经启动并运行了一个外部 Keycloak 服务器,并且 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 设置。

【问题讨论】:

  • 令牌在第一次请求期间位于标头中。
  • 哦,是的,就是这样。嗯。不会改变它仍然产生 401 的事实。
  • 您需要配置中间件以将令牌添加到中心请求。
  • 太棒了。究竟是哪个“中间件”?我在这里使用这些文档:docs.microsoft.com/en-us/aspnet/core/signalr/…

标签: jwt signalr blazor openid-connect webassembly


【解决方案1】:

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

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

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 服务器(请参阅此 StackOverflow 线程)并将 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
            {
[...]

【讨论】:

  • "在协商阶段似乎已经需要 access_token" 你解决了这个问题吗?您的代码是正确且可靠的方法,还是只是一种解决方法?
  • 上面的代码实际上是正确的方法。令牌在协商期间已经在标头中传递,但在实际连接期间无法以这种方式调整标头,因此这种中间件解决方法。真正的罪魁祸首是失踪的option.Audience
猜你喜欢
  • 2020-09-16
  • 2020-11-02
  • 2022-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-16
  • 1970-01-01
  • 2021-09-20
相关资源
最近更新 更多