From 4db3b736f29eca6d76c5d177eb98e1ce37772aee Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 5 May 2025 08:56:08 +0200 Subject: [PATCH 1/3] Backport changes connected with identity template fix. --- .../Account/Pages/ConfirmEmail.razor | 1 + .../Account/Pages/ConfirmEmailChange.razor | 1 + .../Account/Pages/ExternalLogin.razor | 3 +++ .../Account/Pages/ForgotPassword.razor | 1 + .../Account/Pages/Manage/ChangePassword.razor | 12 +++++++++++- .../Pages/Manage/DeletePersonalData.razor | 12 ++++++++++-- .../Account/Pages/Manage/Disable2fa.razor | 9 +++++++-- .../Account/Pages/Manage/Email.razor | 17 ++++++++++++++++- .../Pages/Manage/EnableAuthenticator.razor | 13 ++++++++++--- .../Account/Pages/Manage/ExternalLogins.razor | 18 +++++++++++++++++- .../Pages/Manage/GenerateRecoveryCodes.razor | 11 ++++++++++- .../Account/Pages/Manage/Index.razor | 12 +++++++++++- .../Pages/Manage/ResetAuthenticator.razor | 5 +++++ .../Account/Pages/Manage/SetPassword.razor | 11 ++++++++++- .../Pages/Manage/TwoFactorAuthentication.razor | 5 +++++ .../Account/Pages/RegisterConfirmation.razor | 1 + .../Account/Pages/ResetPassword.razor | 2 ++ 17 files changed, 121 insertions(+), 13 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor index 830ee204c824..4254c0e65bae 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmail.razor @@ -30,6 +30,7 @@ if (UserId is null || Code is null) { RedirectManager.RedirectTo(""); + return; } var user = await UserManager.FindByIdAsync(UserId); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor index 478a810defc9..a1547b6fa41c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ConfirmEmailChange.razor @@ -36,6 +36,7 @@ { RedirectManager.RedirectToWithStatus( "Account/Login", "Error: Invalid email change confirmation link.", HttpContext); + return; } var user = await UserManager.FindByIdAsync(UserId); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor index 0e5200ecb4e5..611b3448d7f6 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ExternalLogin.razor @@ -101,6 +101,7 @@ if (externalLoginInfo is null) { RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext); + return; } // Sign in the user with this external login provider if the user already has a login. @@ -121,6 +122,7 @@ else if (result.IsLockedOut) { RedirectManager.RedirectTo("Account/Lockout"); + return; } // If the user does not have an account, then ask the user to create an account. @@ -135,6 +137,7 @@ if (externalLoginInfo is null) { RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information during confirmation.", HttpContext); + return; } var emailStore = GetEmailStore(); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor index efe20b0f5639..1ab1bfa5fcb0 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ForgotPassword.razor @@ -44,6 +44,7 @@ { // Don't reveal that the user does not exist or is not confirmed RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation"); + return; } // For more information on how to enable account confirmation and password reset please diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor index dc21755a47b0..6278bd9c5e31 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ChangePassword.razor @@ -41,7 +41,7 @@ @code { private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; private bool hasPassword; [CascadingParameter] @@ -53,6 +53,11 @@ protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + hasPassword = await UserManager.HasPasswordAsync(user); if (!hasPassword) { @@ -62,6 +67,11 @@ private async Task OnValidSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; cannot change password."); + } + var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword); if (!changePasswordResult.Succeeded) { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor index d7bfb84d491b..b766b66a5c0e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/DeletePersonalData.razor @@ -40,7 +40,7 @@ @code { private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; private bool requirePassword; [CascadingParameter] @@ -53,11 +53,19 @@ { Input ??= new(); user = await UserAccessor.GetRequiredUserAsync(HttpContext); - requirePassword = await UserManager.HasPasswordAsync(user); + if (user is not null) + { + requirePassword = await UserManager.HasPasswordAsync(user); + } } private async Task OnValidSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to delete personal data."); + } + if (requirePassword && !await UserManager.CheckPasswordAsync(user, Input.Password)) { message = "Error: Incorrect password."; diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor index d3969f457c9c..c64ab96d629c 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Disable2fa.razor @@ -31,7 +31,7 @@ @code { - private ApplicationUser user = default!; + private ApplicationUser? user; [CascadingParameter] private HttpContext HttpContext { get; set; } = default!; @@ -40,7 +40,7 @@ { user = await UserAccessor.GetRequiredUserAsync(HttpContext); - if (HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user)) + if (user is not null && HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user)) { throw new InvalidOperationException("Cannot disable 2FA for user as it's not currently enabled."); } @@ -48,6 +48,11 @@ private async Task OnSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to disable 2FA."); + } + var disable2faResult = await UserManager.SetTwoFactorEnabledAsync(user, false); if (!disable2faResult.Succeeded) { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor index c8b1518061ae..08eb4589b86a 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Email.razor @@ -55,7 +55,7 @@ @code { private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; private string? email; private bool isEmailConfirmed; @@ -68,6 +68,11 @@ protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + email = await UserManager.GetEmailAsync(user); isEmailConfirmed = await UserManager.IsEmailConfirmedAsync(user); @@ -82,6 +87,11 @@ return; } + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to manage email."); + } + var userId = await UserManager.GetUserIdAsync(user); var code = await UserManager.GenerateChangeEmailTokenAsync(user, Input.NewEmail); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); @@ -101,6 +111,11 @@ return; } + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to send verification email."); + } + var userId = await UserManager.GetUserIdAsync(user); var code = await UserManager.GenerateEmailConfirmationTokenAsync(user); code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code)); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor index 1872e9e6c83b..d2e7cdd24813 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/EnableAuthenticator.razor @@ -70,7 +70,7 @@ else private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; private string? sharedKey; private string? authenticatorUri; private IEnumerable? recoveryCodes; @@ -84,12 +84,19 @@ else protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); - - await LoadSharedKeyAndQrCodeUriAsync(user); + if (user is not null) + { + await LoadSharedKeyAndQrCodeUriAsync(user); + } } private async Task OnValidSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to configure authenticator app."); + } + // Strip spaces and hyphens var verificationCode = Input.Code.Replace(" ", string.Empty).Replace("-", string.Empty); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor index 25a6b27926a0..ca95de6402dc 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ExternalLogins.razor @@ -66,7 +66,7 @@ @code { public const string LinkLoginCallbackAction = "LinkLoginCallback"; - private ApplicationUser user = default!; + private ApplicationUser? user; private IList? currentLogins; private IList? otherLogins; private bool showRemoveButton; @@ -86,6 +86,11 @@ protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + currentLogins = await UserManager.GetLoginsAsync(user); otherLogins = (await SignInManager.GetExternalAuthenticationSchemesAsync()) .Where(auth => currentLogins.All(ul => auth.Name != ul.LoginProvider)) @@ -107,6 +112,11 @@ private async Task OnSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to remove external login."); + } + var result = await UserManager.RemoveLoginAsync(user, LoginProvider!, ProviderKey!); if (!result.Succeeded) { @@ -119,11 +129,17 @@ private async Task OnGetLinkLoginCallbackAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to add external login."); + } + var userId = await UserManager.GetUserIdAsync(user); var info = await SignInManager.GetExternalLoginInfoAsync(userId); if (info is null) { RedirectManager.RedirectToCurrentPageWithStatus("Error: Could not load external login info.", HttpContext); + return; } var result = await UserManager.AddLoginAsync(user, info); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor index c63c765ab887..b03a2905200b 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/GenerateRecoveryCodes.razor @@ -40,7 +40,7 @@ else @code { private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; private IEnumerable? recoveryCodes; [CascadingParameter] @@ -49,6 +49,10 @@ else protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } var isTwoFactorEnabled = await UserManager.GetTwoFactorEnabledAsync(user); if (!isTwoFactorEnabled) @@ -59,6 +63,11 @@ else private async Task OnSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to generate recovery codes."); + } + var userId = await UserManager.GetUserIdAsync(user); recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); message = "You have generated new recovery codes."; diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor index bff831a1d852..a5c47d666791 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/Index.razor @@ -34,7 +34,7 @@ @code { - private ApplicationUser user = default!; + private ApplicationUser? user; private string? username; private string? phoneNumber; @@ -47,6 +47,11 @@ protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + username = await UserManager.GetUserNameAsync(user); phoneNumber = await UserManager.GetPhoneNumberAsync(user); @@ -55,6 +60,11 @@ private async Task OnValidSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to set phone number."); + } + if (Input.PhoneNumber != phoneNumber) { var setPhoneResult = await UserManager.SetPhoneNumberAsync(user, Input.PhoneNumber); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor index c12e38094f76..1ae1e884a9a5 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/ResetAuthenticator.razor @@ -37,6 +37,11 @@ private async Task OnSubmitAsync() { var user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + await UserManager.SetTwoFactorEnabledAsync(user, false); await UserManager.ResetAuthenticatorKeyAsync(user); var userId = await UserManager.GetUserIdAsync(user); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor index 79eabe780fff..e56f1980af56 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/SetPassword.razor @@ -39,7 +39,7 @@ @code { private string? message; - private ApplicationUser user = default!; + private ApplicationUser? user; [CascadingParameter] private HttpContext HttpContext { get; set; } = default!; @@ -50,6 +50,10 @@ protected override async Task OnInitializedAsync() { user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } var hasPassword = await UserManager.HasPasswordAsync(user); if (hasPassword) @@ -60,6 +64,11 @@ private async Task OnValidSubmitAsync() { + if (user is null) + { + throw new InvalidOperationException("User is not loaded; failed to set password."); + } + var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!); if (!addPasswordResult.Succeeded) { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor index d15097a9ed16..dab6b95429ec 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor @@ -83,6 +83,11 @@ else protected override async Task OnInitializedAsync() { var user = await UserAccessor.GetRequiredUserAsync(HttpContext); + if (user is null) + { + return; + } + canTrack = HttpContext.Features.Get()?.CanTrack ?? true; hasAuthenticator = await UserManager.GetAuthenticatorKeyAsync(user) is not null; is2faEnabled = await UserManager.GetTwoFactorEnabledAsync(user); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor index ec74ea584ef8..52ee9594920b 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/RegisterConfirmation.razor @@ -46,6 +46,7 @@ else if (Email is null) { RedirectManager.RedirectTo(""); + return; } var user = await UserManager.FindByEmailAsync(Email); diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor index 61e07c0b8de0..4c88c56665ae 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/Pages/ResetPassword.razor @@ -58,6 +58,7 @@ if (Code is null) { RedirectManager.RedirectTo("Account/InvalidPasswordReset"); + return; } Input.Code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code)); @@ -70,6 +71,7 @@ { // Don't reveal that the user does not exist RedirectManager.RedirectTo("Account/ResetPasswordConfirmation"); + return; } var result = await UserManager.ResetPasswordAsync(user, Input.Code, Input.Password); From 63f0095027471465d599ff5ca9b907e2b16c2d66 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Mon, 5 May 2025 11:57:21 +0200 Subject: [PATCH 2/3] Missing commit. --- .../Account/IdentityRedirectManager.cs | 20 +++++++++++-------- .../Account/IdentityUserAccessor.cs | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs index da85e6efd46d..692f51a57386 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs @@ -7,6 +7,12 @@ internal sealed class IdentityRedirectManager(NavigationManager navigationManage { public const string StatusCookieName = "Identity.StatusMessage"; + private const string _enableThrowNavigationException = "Microsoft.AspNetCore.Components.Endpoints.NavigationManager.EnableThrowNavigationException"; + + [FeatureSwitchDefinition("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.EnableThrowNavigationException")] + private static bool _throwNavigationException => + AppContext.TryGetSwitch(_enableThrowNavigationException, out var switchValue) && switchValue; + private static readonly CookieBuilder StatusCookieBuilder = new() { SameSite = SameSiteMode.Strict, @@ -15,7 +21,6 @@ internal sealed class IdentityRedirectManager(NavigationManager navigationManage MaxAge = TimeSpan.FromSeconds(5), }; - [DoesNotReturn] public void RedirectTo(string? uri) { uri ??= ""; @@ -26,13 +31,15 @@ public void RedirectTo(string? uri) uri = navigationManager.ToBaseRelativePath(uri); } - // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. - // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. navigationManager.NavigateTo(uri); - throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} can only be used during static rendering."); + if (_throwNavigationException) + { + // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. + // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. + throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} can only be used during static rendering."); + } } - [DoesNotReturn] public void RedirectTo(string uri, Dictionary queryParameters) { var uriWithoutQuery = navigationManager.ToAbsoluteUri(uri).GetLeftPart(UriPartial.Path); @@ -40,7 +47,6 @@ public void RedirectTo(string uri, Dictionary queryParameters) RedirectTo(newUri); } - [DoesNotReturn] public void RedirectToWithStatus(string uri, string message, HttpContext context) { context.Response.Cookies.Append(StatusCookieName, message, StatusCookieBuilder.Build(context)); @@ -49,10 +55,8 @@ public void RedirectToWithStatus(string uri, string message, HttpContext context private string CurrentPath => navigationManager.ToAbsoluteUri(navigationManager.Uri).GetLeftPart(UriPartial.Path); - [DoesNotReturn] public void RedirectToCurrentPage() => RedirectTo(CurrentPath); - [DoesNotReturn] public void RedirectToCurrentPageWithStatus(string message, HttpContext context) => RedirectToWithStatus(CurrentPath, message, context); } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs index 86e027c0b6ee..077ef1a57fd0 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityUserAccessor.cs @@ -5,7 +5,7 @@ namespace BlazorWeb_CSharp.Components.Account; internal sealed class IdentityUserAccessor(UserManager userManager, IdentityRedirectManager redirectManager) { - public async Task GetRequiredUserAsync(HttpContext context) + public async Task GetRequiredUserAsync(HttpContext context) { var user = await userManager.GetUserAsync(context.User); From 6bb4132e4eb4def89a07222e304ae8e4369e798f Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Tue, 6 May 2025 13:58:18 +0200 Subject: [PATCH 3/3] Feedback: Remove the exception, even when the switch is set. --- .../Components/Account/IdentityRedirectManager.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs index 692f51a57386..a7de94ca41e2 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorWeb-CSharp/BlazorWeb-CSharp/Components/Account/IdentityRedirectManager.cs @@ -7,12 +7,6 @@ internal sealed class IdentityRedirectManager(NavigationManager navigationManage { public const string StatusCookieName = "Identity.StatusMessage"; - private const string _enableThrowNavigationException = "Microsoft.AspNetCore.Components.Endpoints.NavigationManager.EnableThrowNavigationException"; - - [FeatureSwitchDefinition("Microsoft.AspNetCore.Components.Endpoints.NavigationManager.EnableThrowNavigationException")] - private static bool _throwNavigationException => - AppContext.TryGetSwitch(_enableThrowNavigationException, out var switchValue) && switchValue; - private static readonly CookieBuilder StatusCookieBuilder = new() { SameSite = SameSiteMode.Strict, @@ -32,12 +26,6 @@ public void RedirectTo(string? uri) } navigationManager.NavigateTo(uri); - if (_throwNavigationException) - { - // During static rendering, NavigateTo throws a NavigationException which is handled by the framework as a redirect. - // So as long as this is called from a statically rendered Identity component, the InvalidOperationException is never thrown. - throw new InvalidOperationException($"{nameof(IdentityRedirectManager)} can only be used during static rendering."); - } } public void RedirectTo(string uri, Dictionary queryParameters)