简体   繁体   English

Duende BFF Yarn 在使用来自 Blazor WebAssembly 应用程序的 gRpc Web 客户端时不传递令牌

[英]Duende BFF Yarn does not pass tokens when using gRpc Web Client from Blazor WebAssembly app

I am building a web application with ASP.NET Core 6.我正在使用 ASP.NET 核心 6 构建 web 应用程序。
I have:我有:

  1. Frontend.Client - a Blazor WebAssembly with the UI Frontend.Client - 带有 UI 的 Blazor WebAssembly
  2. Frontend.Server - ASP.NET Core, hosting the Blazor WebAssembly Frontend.Server - ASP.NET 核心,托管 Blazor WebAssembly
  3. Web Api - a remote REST Service Web Api - 远程 REST 服务
  4. gRpc Service - a remote gRpc Service gRpc 服务- 远程 gRpc 服务
  5. Identity Provider - a Duende project using Duende.Bff.Yarp Identity Provider - 一个使用 Duende.Bff.Yarp 的 Duende 项目

My Frontend.Client is configured to call its own BFF (the Frontend.Server), while the server forwards the calls to the REST and gRpc services using Duende.Bff.YARP .我的 Frontend.Client 配置为调用自己的 BFF(Frontend.Server),而服务器使用Duende.Bff.YARP将调用转发到 REST 和 gRpc 服务。
The calls to the REST service work as expected: the client passes the token automatically as by documentation.对 REST 服务的调用按预期工作:客户端按照文档自动传递令牌。
My problem is with the calls to gRpc, which don't seem to use the correct HttpClient with the AntiForgeryToken and the Access Token as it should.我的问题是对 gRpc 的调用,它似乎没有使用正确的 HttpClient 和 AntiForgeryToken 和访问令牌。
I know I'm missing some setting somewhere but I can't find any example on how to use Duende with a gRpcWebClient.我知道我在某处遗漏了一些设置,但我找不到任何关于如何将 Duende 与 gRpcWebClient 一起使用的示例。

My Frontend.Client configuration contains:我的Frontend.Client配置包含:

builder.Services.AddScoped<AuthenticationStateProvider, BffAuthenticationStateProvider>();

builder.Services.AddTransient<AntiforgeryHandler>();

builder.Services.AddHttpClient("backend", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<AntiforgeryHandler>();
builder.Services.AddTransient(sp => sp.GetRequiredService<IHttpClientFactory>().CreateClient("backend"));

builder.Services.AddSingleton(services => {
    var backendUrl = new Uri(builder.HostEnvironment.BaseAddress);
    var channel = GrpcChannel.ForAddress(backendUrl, new GrpcChannelOptions {
        HttpHandler = new GrpcWebHandler(new HttpClientHandler()),
    });
    return new Commenter.CommenterClient(channel);
});

My Frontend.Server configuration contains:我的Frontend.Server配置包含:

builder.Services.AddBff();
var proxyBuilder = builder.Services.AddReverseProxy().AddTransforms<AccessTokenTransformProvider>();
// Initialize the reverse proxy from the "ReverseProxy" section of configuration
proxyBuilder.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
builder.Services.AddAuthentication(options => {
    options.DefaultScheme = "cookie";
    options.DefaultChallengeScheme = "oidc";
    options.DefaultSignOutScheme = "oidc";
})
.AddCookie("cookie", options => {
    options.Cookie.Name = "__Host-blazor";
    options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("oidc", options => {
    options.Authority = "https://localhost:5007";

    options.ClientId = "photosharing.bff";
    options.ClientSecret = "A9B27D26-E71C-4C53-89A8-3DAB53CE1854";
    options.ResponseType = "code";
    options.ResponseMode = "query";

    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("profile");
    options.Scope.Add("photosrest");
    options.Scope.Add("commentsgrpc");
    options.Scope.Add("offline_access");

    options.MapInboundClaims = false;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;
});

//code omitted for brevity

app.UseAuthentication();
app.UseBff();
app.UseAuthorization();
app.MapBffManagementEndpoints();

app.MapReverseProxy().AsBffApiEndpoint();

The appsettings.json file used to read the configuration contains:用于读取配置的 appsettings.json 文件包含:

"ReverseProxy": {
    "Routes": {
      "photosrestroute": {
        "ClusterId": "photosrestcluster",
        "Match": {
          "Path": "/photos/{*any}"
        },
        "Metadata": {
          "Duende.Bff.Yarp.TokenType": "User"
        }
      },
      "commentsgrpcroute": {
        "ClusterId": "commentsgrpccluster",
        "Match": {
          "Path": "/comments.Commenter/{*any}"
        },
        "Metadata": {
          "Duende.Bff.Yarp.TokenType": "User"
        }
      }
    },
    "Clusters": {
      "photosrestcluster": {
        "Destinations": {
          "photosrestdestination": {
            "Address": "https://localhost:5003/"
          }
        }
      },
      "commentsgrpccluster": {
        "Destinations": {
          "commentsgrpdestination": {
            "Address": "https://localhost:5005/"
          }
        }
      }
    }
  }

When my client calls the gRpc, I get a 401 Unauthorized response and Duende.Bff logs that the AntiForgery check did not pass, in fact the request does not have the header with the X-CSRF 1 (while the calls to the REST Api do). When my client calls the gRpc, I get a 401 Unauthorized response and Duende.Bff logs that the AntiForgery check did not pass, in fact the request does not have the header with the X-CSRF 1 (while the calls to the REST Api do )。 This would suggest that the gRpc client is not using the HTTP client that Duende uses.这表明 gRpc 客户端没有使用 Duende 使用的 HTTP 客户端。
How do I connect my gRpc client to Duende?如何将我的 gRpc 客户端连接到 Duende?

NOTE: before introducing the Authentication / Authorization bit, I was using YARP directly and the calls to gRpc were working just fine.注意:在引入身份验证/授权位之前,我直接使用 YARP 并且对 gRpc 的调用工作正常。 It's when I added Duende that it broke.当我添加 Duende 时,它就坏了。

The problem was the AntiforgeryHandler, since I had not added it to the chain of the HttpHandlers of my gRpcChannel.问题是 AntiforgeryHandler,因为我没有将它添加到我的 gRpcChannel 的 HttpHandler 链中。 What I did to solve it was我为解决它所做的是

  1. Add a constructor to my AntiforgeryHandler to accept an innerhandler and pass it to its base class向我的 AntiforgeryHandler 添加一个构造函数以接受一个内部处理程序并将其传递给它的基础 class
  2. Attach my AntiforgeryHandler to the chain of HttpHandlers turing the construction of the grpc client将我的 AntiforgeryHandler 附加到 HttpHandlers 链上,从而构建 grpc 客户端

The AntiforgeryHandler becomes: AntiforgeryHandler 变为:

namespace PhotoSharingApplication.Frontend.Client.DuendeAuth;

public class AntiforgeryHandler : DelegatingHandler {
    public AntiforgeryHandler() { }
    public AntiforgeryHandler(HttpClientHandler innerHandler) : base(innerHandler) { }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
    request.Headers.Add("X-CSRF", "1");
    return base.SendAsync(request, cancellationToken);
  }
}

The construction of the grpc client in my Frontend.Client project becomes:我的 Frontend.Client 项目中 grpc 客户端的构建变成:

builder.Services.AddSingleton(services => {
    var backendUrl = new Uri(builder.HostEnvironment.BaseAddress);
    var channel = GrpcChannel.ForAddress(backendUrl, new GrpcChannelOptions {
        HttpHandler = new GrpcWebHandler(new AntiforgeryHandler(new HttpClientHandler())),
    });
    return new Commenter.CommenterClient(channel);
});

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从 Blazor Webassembly 应用程序调用服务时 IIS 中的 Grpc.Core.ClientBase 错误 - Grpc.Core.ClientBase Error in IIS when service invoked from Blazor Webassembly app 调用 Identity Server 时出现 Duende BFF 未经授权的客户端错误。 授予类型代码更改为授予类型客户端凭据 - Duende BFF unauthorized client error when calling Identity Server. Grant Type code changes to Grant Type Client Credentials 在 .net 5 的 Blazor 客户端 WebAssembly 上使用 InputFile - Using InputFile on Blazor client WebAssembly with .net 5 Blazor Webassembly 应用程序显示来自服务器的图像 - Blazor Webassembly App display image from server WebAssembly Blazor 在 IIS 上部署时不使用令牌 - WebAssembly Blazor not using token when deployed on IIS 如何将自定义声明从 Duende IdentityServer 发送到 Blazor WASM 托管应用程序 - How to send custom claims from Duende IdentityServer to Blazor WASM Hosted app 如何在 blazor webassembly 应用程序(客户端)中调用 blazor 服务器应用程序(登录 webapi) - how to call blazor server app(login webapi) in blazor webassembly app(client side) 处置时 Blazor WebAssembly Img http 流请求不会停止 - Blazor WebAssembly Img http stream request does not stop when Disposing 如何在客户端(blazor webassembly)调用create webapi(blazor server app) - how to call create webapi(blazor server app) in client side(blazor webassembly) 从 Blazor WebAssembly 客户端项目中的 GetFromJsonAsync 调用接收 Blazor 服务器项目中 API 方法中的参数 - Receiving parameter in API method in Blazor Server project from GetFromJsonAsync call in Blazor WebAssembly Client project
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM