I am building a web application with ASP.NET Core 6.
I have:
- Frontend.Client - a Blazor WebAssembly with the UI
- Frontend.Server - ASP.NET Core, hosting the Blazor WebAssembly
- Web Api - a remote REST Service
- gRpc Service - a remote gRpc Service
- Identity Provider - a Duende project using Duende.Bff.Yarp
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.
The calls to the REST service work as expected: the client passes the token automatically as by documentation.
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.
I know I'm missing some setting somewhere but I can't find any example on how to use Duende with a gRpcWebClient.
My Frontend.Client configuration contains:
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:
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:
"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). This would suggest that the gRpc client is not using the HTTP client that Duende uses.
How do I connect my gRpc client to Duende?
NOTE: before introducing the Authentication / Authorization bit, I was using YARP directly and the calls to gRpc were working just fine. It's when I added Duende that it broke.