aspnetcore
aspnetcore copied to clipboard
InvalidToken when confirming email change
Is there an existing issue for this?
- [X] I have searched the existing issues
Describe the bug
In a .NET8 Blazor Server Side application I am managing my users - I want to have an option to change emails. I have a Blazor Component for editing a user and there after submitting this code is executed:
var user = await UserManager.FindByIdAsync(Id).ConfigureAwait(true);
var code = await UserManager.GenerateChangeEmailTokenAsync(user, newEmail).ConfigureAwait(true);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
ConfirmEmailChange.cshtml page has this code OnGetAsync:
code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
var result = await this.userManager.ChangeEmailAsync(user, email, code);
And the result is not succeeded, and in result.Errors I get an error InvalidToken.
I have added logging both raw code, encoded, received, and decoded. Raw and decoded codes are identical, and encoded and received codes are also identical. + are still +, they are not changed into spaces, all characters are identical.
This happens when the Application is running on a Linux Server. But when I run it locally on my Windows computer, the email changing works.
However when I try to change my email in the Identity Email.cshtml page, with the following code:
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
Everything works both on Linux and on Windows, it also directs to the ConfirmEmailChange.cshtml page.
So:
why does the userManager.ChangeEmailAsync method work when receiving a:
- windows razor page generated token
- windows blazor component generated token
- linux razor page generated token
but doesn't work on:
- linux blazor component generated token
?
Expected Behavior
No response
Steps To Reproduce
No response
Exceptions (if any)
No response
.NET Version
8.0.200
Anything else?
No response
I have pushed the application in this form to a production server, which is also linux, and there everything works. I will try to find out what is the difference between test linux server and production linux server.
Both servers have system Ubuntu 22.04.3 LTS
The difference that I can see is, that the working production server has some packages like aspnetcore-targeting-pack, dotnet-apphost-pack, dotnet-targeting-pack in version 8.0.2-0 while the not working staging server has them in 8.0.2-1.
Another difference is the dotnet-sdk and the working server has 8.0.101-1, while to not working one has 8.0.200-1
Both apps are run from an identical systemdservice with the only difference being that a different Environment variable is set. This environment mostly just sets a correct connection string and different Serilog sinks. It does not change anything Identity-related apart from the connection string.
The tokens must be generated and validated using the same data protection key. By default, this is stored in the users home directory on Linux. You should see this in ~/.aspnet/DataProtection-Keys/. Can you verify that you're always using the same key used to generate the token when confirming the email change, and that the application name of project generating and validating the token is the same?
Could the issue be that different applications with different names are generating and validating the token? Or that the same application is running on multiple different hosts or as different users which don't share a key?
Thank you for the answer @halter73 - I don't have what you are mentioning configured explicitly. This is my Identity configuration:
builder.Services.AddIdentity<CustomUser, CustomRole>(
options =>
{
options.SignIn.RequireConfirmedAccount = true;
// other pasword requirements configured here
})
.AddEntityFrameworkStores<CustomDbContext>()
.AddDefaultTokenProviders()
.AddUserConfirmation<UserConfirmation<CustomUser>>()
.AddErrorDescriber<CustomIdentityErrorDescriber>()
.AddDefaultUI();
On the working server in~/.aspnet/DataProtection-Keys I have to .xml files, first one was valid thru 8.02.2024, and the second one is currently valid.
On the not working server there are 4 xml files, because it has been running for longer, and the previous key was valid until 18.12.2023
These servers are running only this one application, and they are both deployed from the same repository. The generating and validating are done within the same project, the only difference being that they are generating in a razor component, and validated on a .cshtml razor page. I will add explicit configuration of the Keys to ToFileSystem and will SetApplicationName. When I do that I will let you know if that changed the outcome.
@halter73 I have added this configuration:
builder.Services
.AddDataProtection()
.SetApplicationName("NAME");
This is just one application on the server, on the same host, one instance and always running as root user. I deleted all old keys and restarted the application to make sure that there was just one key generated in /root/.aspnet/DataProtection-Keys. The key was generated there but the issue persists.
If it's a single project, can you please publish it to GitHub so we can quickly try it ourselves on Linux?
@halter73 we cannot make this repository public.
Can you create a simplified project that recreates the issue and publish that to GitHub?
Thank you for filing this issue. In order for us to investigate this issue, please provide a minimal repro project that illustrates the problem without unnecessary code. Please share with us in a public GitHub repo because we cannot open ZIP attachments, and don't include any confidential content.