Search code examples
c#asp.net-coreasp.net-identityblazor-webassembly

UserManager.GenerateEmailConfirmationTokenAsync generated token does not work from other domain


Im trying to create an identity provider, where a user logs in to the application (and is then redirected), and manages their account credentials, and which provides bearer tokens. Separately runs a Blazor WASM web application that manages the application logic, but also has an admin panel in which administrators can create users, reset passwords, manage roles/permissions, etc. This admin panel calls an API.

  • The identity provider runs on https://localhost:5000
  • The web application runs on https://localhost:5001
  • The API on runs https://localhost:5002.

Now, when I create a user in this admin panel, the API is called, which sends a confirmation email that contains a URL containing the email confirmation token, generated by ASP.NET Identity's UserManager.GenerateEmailConfirmationTokenAsync. The token is generated by the API (localhost:5002) calling the class library, but the URL to confirm is to https://localhost:5000/Account/ConfirmEmail?UserId=xxxxxxxxxxxxxxxxx&token=yyyyyyyyyyyyyyyyyyyy. When I click this link, I receive the message: VerifyUserTokenAsync() failed with purpose: EmailConfirmation for user xxxxxxx-xxxxxx-xxxxxx-xxxx-xxxxxxxxxxxxxx. with the error message: "InvalidToken".

The identity provider runs ASP.NET Identity, IdentityServer4, and is written in ASP.NET Core 3.1. The application is written in Blazor (which is netstandard2.1), which then calls an API (ASP.NET Core 3.1), which calls a class library (ASP.NET Core 3.1). All projects use the same DatabaseContexts.

I've tried setting

services.AddDataProtection()
    .PersistKeysToFileSystem(new DirectoryInfo(Configuration["AppSettings:keydir"]))
    .SetApplicationName(Configuration["AppSettings:keyname"]);

in both the identity provider and the API, but no luck. I also tried setting:

services.AddDataProtection(options =>
    {
        options.ApplicationDiscriminator = "myapp";
    })

which is similar to this issue: https://github.com/dotnet/aspnetcore/issues/22081

Im thinking it might be due to the extra class library I'm calling, do I have to set some DataProtection setting there?


Solution

  • I figured it out. The different projects were not the problem, neither was the dataprotection. Turned out I generated a confirmationtoken before I had created the user. This way the generated token is based on an empty securitystamp. Then in the other project, the usermanager validates the token against a token based on a non-null securitystamp, because now the user had been created. This of course yields an invalid token.