I am using the Identity scaffolding to build a Blazor server-based website.
One of my customers is so bugged with the default "complex" password requirements, stating it makes the whole thing "unusable". Instead of lowering the password standard, I proposed a solution that use a password generator built-in on the registration page.
So I added a button to generate the password, but the button never ever worked. I set a breakpoint on the randomPassword() method but it was never hit.
This StackOverflow question pointed out that The newly introduced RenderMode settings is to blame, so I added @rendermode InteractiveServer on this page. Unfortunately, this didn't solve the problem, but also introduced a new one. I now have a NullReferenceException originating from \Components\Account\Shared\StatusMessage.razor. The HTTPContext is null for some reason.
随机密码 @* This doesn't work at all *@
注册
}
else
{
新建用户
新建用户
注册
}
@code {
private IEnumerable? identityErrors;
[SupplyParameterFromForm]
private InputModel Input { get; set; } = new();
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}";
private bool initialize => GetAdminCount().Result == 0;
private async Task RegisterUser(EditContext editContext)
{
var user = CreateUser();
await UserStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);
var emailStore = GetEmailStore();
await emailStore.SetEmailAsync(user, Input.UserName, CancellationToken.None);
user.DepartmentName = Input.Department;
var result = await UserManager.CreateAsync(user, Input.Password);
if (!result.Succeeded)
{
identityErrors = result.Errors;
return;
}
if (initialize)
{
var addRoleResult = await RoleManager.CreateAsync(new IdentityRole(Constants.AdminRole));
if (!addRoleResult.Succeeded)
{
identityErrors = addRoleResult.Errors;
return;
}
addRoleResult = await UserManager.AddToRoleAsync(user, Constants.AdminRole);
if (!addRoleResult.Succeeded)
{
identityErrors = addRoleResult.Errors;
return;
}
await SignInManager.SignInAsync(user, isPersistent: true);
RedirectManager.RedirectTo("/");
}
else
{
RedirectManager.RedirectTo("/UserManagement");
}
Logger.LogInformation("User created a new account with password.");
// var userId = await UserManager.GetUserIdAsync(user);
// var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
// code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
// var callbackUrl = NavigationManager.GetUriWithQueryParameters(
// NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
// new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
// await EmailSender.SendConfirmationLinkAsync(user, Input.UserName, HtmlEncoder.Default.Encode(callbackUrl));
// if (UserManager.Options.SignIn.RequireConfirmedAccount)
// {
// RedirectManager.RedirectTo(
// "Account/RegisterConfirmation",
// new() { ["email"] = Input.UserName, ["returnUrl"] = ReturnUrl });
// }
}
private ApplicationUser CreateUser()
{
try
{
return Activator.CreateInstance();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
$"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor.");
}
}
private IUserEmailStore GetEmailStore()
{
if (!UserManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore)UserStore;
}
private sealed class InputModel
{
[Required]
[Display(Name = "登录用户名")]
public string UserName { get; set; } = "";
[Required]
[Display(Name = "科室")]
public string Department { get; set; } = "";
[Required]
[StringLength(100, ErrorMessage = "{0}长度最少为{2}位,最大为{1}位。", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "密码")]
public string Password { get; set; } = "";
[DataType(DataType.Password)]
[Display(Name = "再输一遍密码")]
[Compare("Password", ErrorMessage = "两个密码不一致。")]
public string ConfirmPassword { get; set; } = "";
}
private async Task GetAdminCount()
{
var usersInAdminRole = await UserManager.GetUsersInRoleAsync(Constants.AdminRole);
return usersInAdminRole.Count;
}
private void randomPassword()
{
// This never get's executed
var password = AuthUtils.GenerateRandomPassword();
Input.Password = password;
Input.ConfirmPassword = password;
Logger.LogInformation("randompassword: "+ password);
}
}
```
Then I tried to change the render mode in App.razor to InteractiveServer. Notice that the render mode was set to null when the route starts with /Account with the scaffolding. Unfortunately, things are even worse with the page being completely unusable, refershing itself once per second and with a memory leak about 1MB per second.
Ask a question
I am using the Identity scaffolding to build a Blazor server-based website.
One of my customers is so bugged with the default "complex" password requirements, stating it makes the whole thing "unusable". Instead of lowering the password standard, I proposed a solution that use a password generator built-in on the registration page.
So I added a button to generate the password, but the button never ever worked. I set a breakpoint on the
randomPassword()
method but it was never hit.This StackOverflow question pointed out that The newly introduced RenderMode settings is to blame, so I added
@rendermode InteractiveServer
on this page. Unfortunately, this didn't solve the problem, but also introduced a new one. I now have aNullReferenceException
originating from\Components\Account\Shared\StatusMessage.razor
. The HTTPContext isnull
for some reason.This is the code in question:
初始化
新建用户
Then I tried to change the render mode in
App.razor
to InteractiveServer. Notice that the render mode was set tonull
when the route starts with/Account
with the scaffolding. Unfortunately, things are even worse with the page being completely unusable, refershing itself once per second and with a memory leak about 1MB per second.For your reference, this should be the template: https://github.com/dotnet/Scaffolding/blob/main/src/Scaffolding/VS.Web.CG.Mvc/Templates/BlazorIdentity/Pages/Register.tt
To solve this problem, I could:
But the ultimate question is, why Identity doesn't with InteractiveServer render mode?
Include provider and version information
Microsoft.VisualStudio.Web.CodeGeneration.Design version: 8.0.2 Target framework: .NET 8 Operating system: Windows 11 Pro IDE: (e.g. Visual Studio 2022 17.10.0 Preview 7.0)