From 1f19a5fea68aafe7e36adb10c5f061156d292e70 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 27 May 2026 08:54:40 +0800 Subject: [PATCH 1/3] Show invalid password error on login when user should change password --- .../Pages/Account/Login.cshtml.cs | 15 ++++++++ .../AspNetCore/AbpSignInManager_Tests.cs | 36 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index cef45c2f4f..cc5dc12a27 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -130,6 +130,21 @@ public class LoginModel : AccountPageModel if (result.IsNotAllowed) { + var notAllowedUser = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? + await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); + if (notAllowedUser != null) + { + using (CurrentTenant.Change(notAllowedUser.TenantId)) + { + await IdentityOptions.SetAsync(); + if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) + { + Alerts.Danger(L["InvalidUserNameOrPassword"]); + return Page(); + } + } + } + Alerts.Warning(L["LoginIsNotAllowed"]); return Page(); } diff --git a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs index 7aa540f420..ab2adb6c2f 100644 --- a/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs +++ b/modules/identity/test/Volo.Abp.Identity.AspNetCore.Tests/Volo/Abp/Identity/AspNetCore/AbpSignInManager_Tests.cs @@ -1,8 +1,10 @@ -using System.Net; +using System; +using System.Net; using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Shouldly; +using Volo.Abp.Uow; using Xunit; namespace Volo.Abp.Identity.AspNetCore; @@ -45,4 +47,36 @@ public class AbpSignInManager_Tests : AbpIdentityAspNetCoreTestBase result.ShouldBe("NotAllowed"); } + + [Fact] + public async Task Should_Return_NotAllowed_For_User_That_Should_Change_Password_Regardless_Of_Password() + { + // PreSignInCheck rejects users with ShouldChangePasswordOnNextLogin=true before the + // password is verified, so PasswordSignInAsync returns NotAllowed for both right and + // wrong passwords. Callers that surface a login error to the user (Login page) need + // to recheck the password themselves to distinguish the two cases. + var userManager = GetRequiredService(); + var unitOfWorkManager = GetRequiredService(); + + const string userName = "must-change-password"; + const string password = "1q2w3E*"; + + using (var uow = unitOfWorkManager.Begin()) + { + var user = new IdentityUser(Guid.NewGuid(), userName, userName + "@abp.io"); + user.SetShouldChangePasswordOnNextLogin(true); + (await userManager.CreateAsync(user, password)).Succeeded.ShouldBeTrue(); + await uow.CompleteAsync(); + } + + var withWrongPassword = await GetResponseAsStringAsync( + $"api/signin-test/password?userName={userName}&password=WRONG_PASSWORD" + ); + withWrongPassword.ShouldBe("NotAllowed"); + + var withCorrectPassword = await GetResponseAsStringAsync( + $"api/signin-test/password?userName={userName}&password={password}" + ); + withCorrectPassword.ShouldBe("NotAllowed"); + } } From eefdad7402e28a0ca6930e8d27593e8c290c0b9c Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 27 May 2026 09:44:59 +0800 Subject: [PATCH 2/3] Limit re-check to ShouldChangePassword users to avoid leaking password oracle on other NotAllowed branches --- .../src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs index cc5dc12a27..57fcbe9e46 100644 --- a/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs +++ b/modules/account/src/Volo.Abp.Account.Web/Pages/Account/Login.cshtml.cs @@ -137,7 +137,9 @@ public class LoginModel : AccountPageModel using (CurrentTenant.Change(notAllowedUser.TenantId)) { await IdentityOptions.SetAsync(); - if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) + if ((notAllowedUser.ShouldChangePasswordOnNextLogin || + await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser)) && + !await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) { Alerts.Danger(L["InvalidUserNameOrPassword"]); return Page(); From f8772d2f57c63df7c3ccdf925882ccd17464b2e0 Mon Sep 17 00:00:00 2001 From: maliming Date: Wed, 27 May 2026 10:57:54 +0800 Subject: [PATCH 3/3] Add validation for invalid password on login for users required to change password --- .../IdentityServerSupportedLoginModel.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs index 9e3fff950b..676f37aa47 100644 --- a/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs +++ b/modules/account/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/IdentityServerSupportedLoginModel.cs @@ -159,6 +159,23 @@ public class IdentityServerSupportedLoginModel : LoginModel if (result.IsNotAllowed) { + var notAllowedUser = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? + await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); + if (notAllowedUser != null) + { + using (CurrentTenant.Change(notAllowedUser.TenantId)) + { + await IdentityOptions.SetAsync(); + if ((notAllowedUser.ShouldChangePasswordOnNextLogin || + await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser)) && + !await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) + { + Alerts.Danger(L["InvalidUserNameOrPassword"]); + return Page(); + } + } + } + Alerts.Warning(L["LoginIsNotAllowed"]); return Page(); }