openiddict / openiddict-samples

.NET samples for OpenIddict
https://documentation.openiddict.com/
Apache License 2.0
676 stars 289 forks source link

Integrated OpenIddict server into vanilla .NET 8 Identity Web UI #321

Open karlschriek opened 1 month ago

karlschriek commented 1 month ago

Confirm you've already contributed to this project or that you sponsor it

Version

5.4.0

Question

Background

We are currently working on using the latest .NET identity web UI (which you can get bundled in your IDE when you create a .NET 8 Blazor Web App) in combination with OpenIddict.

The latest template provides a good base for creating a production-ready UI for users to interact with the IdP, including doing things like registering MFA devices, resetting passwords, confirming email addresses and a whole lot more. (You can read more about it here https://devblogs.microsoft.com/dotnet/whats-new-with-identity-in-dotnet-8/#the-blazor-identity-ui)

Our goal was to take this template as a starting point and then integrate OpenIddict into it. Since OpenIddict fundamentally uses the AspNet identity classes, and extends them with entities such as Appplication, Token etc. we hoped that this would be simple to do (even if for someone who is not intimately familiar with either framework). To a large extent this is true, and the result of that is a sample that can be found here:

https://github.com/karlschriek/openiddict-blazor-server-sample/tree/main/OpenIddict.Blazor.Server

(For comparison, here is what the vanilla identity UI we worked from looks like: https://github.com/karlschriek/openiddict-blazor-server-sample/tree/main/VanillaIdentityUI.Blazor.Server)

In addition to the above, this sample also includes a few other things that we have worked on in the past, but these are not directly relevant to this issue

Where we need help

While we were able to get the login flows to work correctly, we are having trouble with the anti-forgery token (although I suspect this might rather just be a symptom of something more fundamental that is misconfigured). The identity UI has several places where it uses the anti-forgery token. The simplest one to test against is the "logout" button.

The moment you navigate to https://localhost:7143/ (where the "OpenIddict.Blazor.Server" sample project is hosted) I can see the anti-forgery token being set, with a name like ".AspNetCore.Antiforgery.hLXdGIjSYB8". As soon as the user logs in, the ".AspNetCore.Identity.Application" cookie is also set. If I now logout, I get:

An unhandled exception occurred while processing the request.
AntiforgeryValidationException: The provided antiforgery token was meant for a different claims-based user than the current user.
Microsoft.AspNetCore.Antiforgery.DefaultAntiforgery.ValidateTokens(HttpContext httpContext, AntiforgeryTokenSet antiforgeryTokenSet)

BadHttpRequestException: Invalid anti-forgery token found when reading parameter "string returnUrl" from the request body as form.
Microsoft.AspNetCore.Http.RequestDelegateFactory+Log.InvalidAntiforgeryToken(HttpContext httpContext, string parameterTypeName, string parameterName, Exception exception, bool shouldThrow)

Similar errors occur on other Blazor components that also require <AntiforgeryToken/>.

This makes me think that either something that we switched off (such as the ".AddIdentityCookies();" extension, which causes an error if use in conjunction with the OpenIddict setup we've added) or some other fundamental mismatch is happening. Would you be able to help us get this sample working correctly? It would go a long way to us being able to take OpenIddict into production.

kevinchalet commented 1 month ago

Hey,

Since OpenIddict fundamentally uses the AspNet identity classes, and extends them with entities such as Appplication, Token etc. we hoped that this would be simple to do (even if for someone who is not intimately familiar with either framework).

To clarify, while OpenIddict uses an approach similar to Identity for its persistence story (entities, stores), it actually doesn't depend on Identity at all (and doesn't use it).

Would you be able to help us get this sample working correctly?

I'll give the sample a try later today but here's what I think is happening:

TL;DR, try to move app.UseAntiforgery() after app.UseAuthorization() to see if it helps.

karlschriek commented 1 month ago

Thanks for the clarification on OpenIddict's use of "Identity", I will definitely keep that in mind.

Also... that fix was much simpler than I expected the answer to be! You are absolutely right, placing app.UseAntiforgery() after app.UseAuthorization() fixes it. (The biggest tragedy of all this is that is that I had misread an error message that actually told me this a few days ago; I went back and recreated it just now and there it was in black and white)

By the way, if you find the sample useful I can also prepare it for this repo. Our full sample is actually a bit more extensive though, and consists of three services:

  1. Authorization Server (i.e. this project)
  2. Client application (also a fully permissioned Blazor Server app, which federates to OpenIddict)
  3. Downstream API (a webapi for which the client application can request an OpenIddict access token

Basically in our use case OpenIddict acts as a central authorization service within a microservices ecosystem. It controls login into various potential client-facing applications, which also call various different downstream WebApis on behalf of the user (using access token).

Let me know if you would be interested in such a sample.