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.37k stars 9.99k forks source link

Cannot attach tokens to outgoing HttpClient requests #53798

Closed jdh148 closed 7 months ago

jdh148 commented 8 months ago

Is there an existing issue for this?

Describe the bug

I am following these instructions from the documentation for attaching authentication tokens to outgoing HTTP requests:

However when using BaseAddressAuthorizationMessageHandler, no token is attached to the outgoing API requests. Using a custom AuthorizationMessageHandler as described does not work at all; the request doesn't even send to the API.

Either something is broken or the documentation is unclear. I attempted to report this as a documentation issue but they told me to file it here.

Expected Behavior

Authorization: Bearer token should be attached to outgoing request

Steps To Reproduce

https://github.com/jdh148/TestBlazorWasmAuth

The last commit attempts to use a custom AuthorizationMessageHandler and doesn't work at all; after login and going to the Weather page, it gets stuck in a continuous loop reloading the page. The second-to-last commit attempts to use the BaseAddressAuthorizationMessageHandler with the base address modified to be the API. The outgoing API request is sent but is missing the Authorization token.

You will need to modify the appsettings.json to use an available OIDC server and client id.

Exceptions (if any)

System.Net.Http.HttpRequestException: Response status code does not indicate success: 401 (Unauthorized).

.NET Version

8.0.101

Anything else?

cc: @guardrex https://github.com/dotnet/AspNetCore.Docs/issues/31598

guardrex commented 8 months ago

Thanks @jdh148 ... I'll be 👀 on here to see where this goes for potential doc updates.

chulla commented 8 months ago

I have the same problem with a component in auto interactive mode, which queries an API with jwt (azure b2c)

When I access the component in automatic interactive mode:

1º The app access in interactive server mode... access token... OK 2º Then the mode changes from Blazor Server mode to Blazor Wasm 3º The component throws a 401 non-authentication exception.

I have tried to attach IAccessToken, but the client does not have MSAL configured... authentication is done in Server mode that obtains authentication using OpenIDC.

I use an implementation of PersistentAuthenticationStateProvider to transfer state (UserInfo) from server mode to wasm mode.

How can I use the access token when switching to wasm mode?

javiercn commented 8 months ago

@jdh148 thanks for contacting us.

The repro you provided doesn't seem to have the code you are describing. In general, AddOidcAuthentication deals with handling the auth process completely client-side without the server being involved. Depending on what you are doing, you might want to use a different pattern like BFF, retrieve the tokens on the server and store them in a cookie.

ghost commented 8 months ago

Hi @jdh148. We have added the "Needs: Author Feedback" label to this issue, which indicates that we have an open question for you before we can take further action. This issue will be closed automatically in 7 days if we do not hear back from you by then - please feel free to re-open it if you come back to this issue after that time.

jdh148 commented 8 months ago

The repro you provided doesn't seem to have the code you are describing. In general, AddOidcAuthentication deals with handling the auth process completely client-side without the server being involved. Depending on what you are doing, you might want to use a different pattern like BFF, retrieve the tokens on the server and store them in a cookie.

Hi @javiercn

The repro I provided contains two projects, an API and a Blazor WASM. The problem is in Blazor WASM, on the client-side.

I am trying to make Blazor WASM send the authentication token in it's API requests. Blazor is successfully authenticating with OIDC, receiving the token, and storing it in session storage as well as cookies. But Blazor is failing to send the token in it's API requests.

mkArtakMSFT commented 8 months ago

@halter73 do you have a sample available we can point @jdh148 to?

halter73 commented 8 months ago

https://learn.microsoft.com/en-us/aspnet/core/blazor/security/server/blazor-web-app-with-oidc?view=aspnetcore8.0&pivots=with-bff-pattern has our documentation on how to implement the Backend for Frontend (BFF) pattern in a Blazor app with the auto render mode.

Since authentication is done on the server instead of the client, the client does not have any tokens available to it. The client needs to use cookies for authentication because tokens cannot be sent for the initial prerendering request from the browser. While it's technically possible to also transmit the token from the server to the client manually, we DO NOT recommend it, because it weakens security. Keeping the token on the server reduces the risk of tokens being exfiltrated via an XSS attack which means some providers like Micrsoft Entra allow for longer token lifetimes.

Instead, the recommended approach is to fallow the BFF pattern and have the WASM service make cookie authenticated requests to the host and then proxy that request with the access token from the host to the final destination. That's demonstrated using YARP's MapForwarder method in the doc sample. If you have multiple endpoints that you need to forward requests to, you can use a "/{**catch-all}" route rather than one for a specific endpoint like "/weatherforecast".

jdh148 commented 8 months ago

Thanks for the additional details. Unless I'm misunderstanding something, this all leads to using Blazor Server or hybird/auto render mode. I am trying to do a pure Blazor WebAssembly application talking to a Web API, with OIDC off to the side for authentication. Is that just not possible with Blazor? The documentation seems to suggest this is possible, but it doesn't work. Can someone explain why the documentation I'm following (the two articles linked in the original description) doesn't work?

halter73 commented 7 months ago

Hopefully https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/standalone-with-microsoft-accounts?view=aspnetcore-8.0 meets your needs for how to do OIDC with WebAssembly only.