dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.15k stars 9.92k forks source link

[Blazor] [Documentation] Example project referenced in the documentation doesn't work #56779

Closed hades200082 closed 1 month ago

hades200082 commented 1 month ago

Is there an existing issue for this?

Describe the bug

I've followed the example code from https://github.com/dotnet/blazor-samples/tree/main/8.0/BlazorWebAppOidc which is linked to from the doccumentation at https://learn.microsoft.com/en-us/aspnet/core/blazor/security/blazor-web-app-with-oidc?view=aspnetcore-8.0&pivots=without-bff-pattern

Following the example, it doesn't work at all. I get errors about the AuthenticatedHttpClientHandler not being registered in DI in the Blazor.Client project.

Adding builder.Services.AddScoped<AuthenticatedHttpClientHandler>(); fixes that, and the server-side authentication seems to then work. But when trying to use a named HttpClient configuration via IHttpClientFactory I get errors that IAccessTokenProvider is not registered.

Also, as an aside, that sample project puts the layout, nav, routes, etc. all under the Blazor.Client project but the default template when starting a new project has them in the Blazor Server project. I tried moving them to more closely follow the example, but this resulted in more errors and things that couldn't be referenced.

Expected Behavior

Documentation should be correct, up to date and easy to follow.

Right now I'm on the verge of giving up on Blazor (again - I have tried to use it several times over the years). I just feel like even when I follow the docs it doesn't work and that the line about "Use your existing .NET skills to build front ends" is downright misleading at this point.

Steps To Reproduce

No response

Exceptions (if any)

No response

.NET Version

8.0.6

Anything else?

No response

mkArtakMSFT commented 1 month ago

Thanks for contacting us, but the sample that we've built works as is. Also, the AuthenticatedHttpClientHandler is not something used by the sample. So perhaps you've made some changes to it which require registration of that services and some other changes that cause the issues you're facing. Hence, we recommend raising questions about fixes in your code in our Discussions so that community members can help you with resolving those issues.

halter73 commented 1 month ago

Also, as an aside, that sample project puts the layout, nav, routes, etc. all under the Blazor.Client project but the default template when starting a new project has them in the Blazor Server project. I tried moving them to more closely follow the example, but this resulted in more errors and things that couldn't be referenced.

This is probably because the BlazorWebAppOidc was based on the template with "global interactivity" instead of "per-page interactivity". You can run the template in both modes to see how they're different. The biggest difference is which project contains the MainLayout.razor and Routes.razor. When Routes.razor is in the client project, you can do client-side routing. The BlazorWebAppOidc samples uses "global interactivity" because it's a little more complicated.

hades200082 commented 1 month ago

@mkArtakMSFT you're correct that the HttpHandler is something I added - but it should be possible with OIDC to get the token to authenticate API requests, otherwise, what's the point of having authentication?

@halter73 thanks, that explains things a little more. As I haven't managed to get past basic config and authentication setup yet and start building my app, I'll take another look when I'm next working on it.

However, from what I've read, "global interactivity" defaults everything to server-rendered and overrides the per-component setting. What's the point of having wasm in there at all in this case?

All I'm trying to do is enable OIDC authentication and set up a named HttpClient such that I can make authenticated requests to my separate API using bearer JWT - creating the HttpClient in the wasm project should ensure that it's available to both wasm and server so the components can work no matter where they are rendered. But I just keep getting errors about things like IAccessTokenProvider not being registered or "The specified cast is not valid" when trying to get a HttpClient from the HttpClientFactory, and the documentation doesn't go far enough with the examples to show this kind of use-case.

Over the years I've had a number of attempts to learn Blazor. I've been working with .NET for over 15 years and Blazor is the only time I've been completely frustrated with it.

I'm trying to push myself to get through these issues this time, but so far it's taken so long to get nowhere and I know that had I chosen to build the front end in NextJS instead I'd be mostly done by now.

Everything I've tried to do in "the .NET way" hasn't worked. The whole "just use your existing .NET skills to build front end" feels like nonsense at this point.

I really want to like Blazor. It feels like it has a lot of potential. It's just so frustrating right now to not be able to get anywhere with it.

mkArtakMSFT commented 1 month ago

@hades200082 there are a few samples. This one demonstrates exactly what you're trying to do: https://github.com/dotnet/blazor-samples/tree/main/8.0/BlazorWebAppOidcBff

The WeatherForecast page retrieves the data to show from a protected endpoint, using an HttpClient. Please note, that the JWT token is not explicitly set on the HttpClient instance, because the authentication information is passed from the server to the client via Cookie, which automatically transfers back to the server on any subsequent HttpClient request. This model is more secure and that's why we adopted it in our samples.

hades200082 commented 1 month ago

I'm my case though, the API is a separate project / application. It is not part of the Blazor application and is hosted on a different domain making the cookie approach useless.

dotnet-policy-service[bot] commented 1 month ago

This issue has been resolved and has not had any activity for 1 day. It will be closed for housekeeping purposes.

See our Issue Management Policies for more information.

hades200082 commented 4 weeks ago

@mkArtakMSFT

The issue with the template is that it doesn't go far enough to enable usage of the InteractiveAuto rendering mode with external APIs.

Take the following use-case.

We have an existing REST API built in .NET 8 using controllers. It already exists in production and currently has several different applications that use it.

The API is authenticated using JWT Bearer tokens from our SSO Identity Server (OIDC)

I've been trying to create a Blazor client for the API using interactive auto rendering to benefit from SSR pre-render but also have the majority of API calls executed on the client side (this helps ensure our IP-based rate limiting works on the unauthenticated endpoints).

So far I have found no way to persist the user's access token from the server to the client and allow both pre-rendering and client rendering to work for components that need to hit the API while also ensuring that the token is refreshed correctly on either side.

Right now, InteractiveAuto is a nice concept but completely unfit for real-world scenarios involving external APIs.