Open Closed

Updating of password setting is not working #10719


User avatar
0
Hon-Tre_IFS created

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

9 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • This is a known Angular UI issue (settings are saved, but the password validation UI/behavior doesn’t reflect the updated policy correctly in some versions).
    • Fix: upgrade the Identity config package used by the Angular UI, then reinstall node modules to ensure the new package is actually applied.

    Steps:

    1. In your Angular project folder, run:
    yarn upgrade @volo/abp.ng.identity.config
    
    1. Delete your lock file and reinstall packages:
    • remove yarn.lock (or package-lock.json)
    • then run:
    yarn install
    
    1. Rebuild and restart the Angular app.

    Sources:

    • https://abp.io/support/questions/166

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    The screenshot alone isn't enough for us to tell what's going wrong. Could you share a bit more so we can reproduce it:

    1. ABP version, UI type (Angular / MVC / Blazor) and whether the solution is tiered (separated auth server).
    2. What exactly does "not working" look like — does Save fail with an error, or does it save successfully but the new policy doesn't take effect when a user actually changes their password?
    3. Are you updating the host settings or a tenant's settings? Does the same happen on the other side?
    4. If Save fails, the browser DevTools → Network response for the settings update request, plus any related server-side log.
    5. If Save succeeds but the policy isn't applied, the exact steps you take afterwards (e.g. change password from My Account, create a user from admin UI) and what message you see.

    If the logs / repro are too large for the thread, feel free to send them to liming.ma@volosoft.com.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Hon-Tre_IFS created

    my Abp Version 10 , Angular

    I have Updated the Host setting to 8 Characters Password Input but it still reflecting 6 Characters in model validation

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Hon-Tre_IFS created

    Still it is asking for the 6 characters input for the Tenant Setting as well updated to 8

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Hon-Tre_IFS created

    Please refer the Abp Setting Table in db

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Thanks for the DB query — the rows look correct (host writes T + NULL, tenants write T + <tenantId>), so the value 8 is saved. The screenshot still showing 6 looks like a different code path or stale state at validation time.

    Could you share:

    1. Which user are you logged in as when you hit Change Password — host admin, or a tenant user?
    2. The URL of the Change Password page in the screenshot — /account/change-password, /Account/ChangePassword, or somewhere else?
    3. Did you do a full browser refresh (F5) between saving the setting and opening Change Password?
    4. Server-side debug log for the failing change-password request — enable Volo.Abp.Settings and Volo.Abp.Identity at Debug level following https://abp.io/support/questions/8622/How-to-enable-Debug-logs-for-troubleshoot-problems. The log will show the actual RequiredLength value used at validation time.
    5. Any customizations on IdentitySettingsAppService, SettingProvider, the Change Password page or ProfileAppService.ChangePasswordAsync?

    Feel free to send logs to liming.ma@volosoft.com if too large for the thread.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    One more question — are you able to reproduce this in a fresh ABP template project (same version, Angular, Pro), or does it only happen in your PRIM project? If you can reproduce in a clean template, please share the exact steps; if not, it likely points to something specific in your project (custom services, modules, or middleware) and we can narrow down from there.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    Hon-Tre_IFS created

    I have tried to reproduce the issue in New fresh Abp Template project I did not found any issue. I have done the customizations Change password Screen I am adding Latest code of Change password model Please verify does it need any change

    using Hon.IFS.SiteHost; using Hon.IFS.SiteHost.EmailNotifications; using Hon.IFS.SiteHost.Pages.Account; using Hon.IFS.SiteHost.StringEncryption; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; using Serilog.Core; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Volo.Abp.Account.Public.Web.Pages.Account; using Volo.Abp.Auditing; using Volo.Abp.Identity; using Volo.Abp.Security.Encryption; using Volo.Abp.Validation; using Volo.Saas.Tenants; using static Volo.Abp.Identity.Settings.IdentitySettingNames; using static Volo.Saas.Host.SaasHostPermissions; using IdentityUser = Volo.Abp.Identity.IdentityUser;

    namespace Volo.Abp.Account.Web.Pages.Account;

    public class ChangePasswordModel : AccountPageModel { private readonly SignInManager<IdentityUser> _signInManager; private readonly UserManager<IdentityUser> _userManager; public IIdentityUserRepository _identityUserRepository; private readonly IConfiguration _configuration; private readonly IEmailNotificationAppService? _emailService; private readonly ITenantRepository _tenantRepository; private readonly ILogger<ChangePasswordModel> _logger;

    [BindProperty]
    [Required(ErrorMessage = "Current password is required.")]
    [DataType(DataType.Password)]
    public string CurrentPassword { get; set; } = null!;
    
    [BindProperty]
    [Required(ErrorMessage = "New password is required.")]
    [DataType(DataType.Password)]
    [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
    public string NewPassword { get; set; } = null!;
    
    [Required]
    [HiddenInput]
    [BindProperty(SupportsGet = true)]
    public Guid UserId { get; set; }
    
    [Required]
    [HiddenInput]
    [BindProperty(SupportsGet = true)]
    public string TenantId { get; set; } = null!;
    
    [BindProperty]
    [Required(ErrorMessage = "Confirm new password is required.")]
    [DataType(DataType.Password)]
    [Compare("NewPassword", ErrorMessage = "Passwords do not match.")]
    [DynamicStringLength(typeof(IdentityUserConsts), nameof(IdentityUserConsts.MaxPasswordLength))]
    public string ConfirmNewPassword { get; set; } = null!;
    
    [HiddenInput]
    [BindProperty(SupportsGet = true)]
    public string ReturnUrl { get; set; } = null!;
    
    [HiddenInput]
    [BindProperty(SupportsGet = true)]
    public string ReturnUrlHash { get; set; } = null!;
    
    [BindProperty(SupportsGet = false)]
    public string? TimezoneId { get; set; }
    
    public ChangePasswordModel(
        UserManager&lt;IdentityUser&gt; userManager,
        SignInManager&lt;IdentityUser&gt; signInManager, IIdentityUserRepository identityUserRepository,
        IConfiguration configuration, ITenantRepository tenantRepository, IEmailNotificationAppService emailService, ILogger&lt;ChangePasswordModel&gt; logger)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _identityUserRepository = identityUserRepository;
        _configuration = configuration;
        _tenantRepository = tenantRepository;
        _emailService = emailService;
        _logger = logger;
        
    }
    
    public IActionResult OnGet([FromQuery] Guid userId, [FromQuery] Guid tenantId)
    {
        UserId = userId;
    
        return Page();
    }
    
    public async Task&lt;IActionResult&gt; OnPostAsync()
    {
     
        using (CurrentTenant.Change(new Guid(TenantId)))
        {
            var user = await _identityUserRepository.FindAsync(UserId);
            if (user == null)
            {
                throw new AbpException("User not found.");
            }
    
            if (string.IsNullOrWhiteSpace(NewPassword))
            {
                ModelState.AddModelError(nameof(NewPassword), "Password cannot be empty.");
                return Page();
            }
    
            if (string.IsNullOrWhiteSpace(ConfirmNewPassword))
            {
                ModelState.AddModelError(nameof(ConfirmNewPassword), "Confirm password cannot be empty.");
                return Page();
            }
    
            if (!string.IsNullOrWhiteSpace(NewPassword) && !string.IsNullOrWhiteSpace(ConfirmNewPassword))
            {
                if (NewPassword.Trim() != ConfirmNewPassword.Trim())
                {
                    ModelState.AddModelError(
                        nameof(ConfirmNewPassword),
                        L["Password and Confirm password do not match."]
                    );
                    return Page();
                }
                
            }
    
            var changePasswordResult = await _userManager.ChangePasswordAsync(user, CurrentPassword, NewPassword);
    
            // Only proceed if password change was successful
            if (changePasswordResult.Succeeded)
            {
                _logger.LogInformation("Password successfully changed for user {Username} (ID: {UserId})", user.UserName, user.Id);
    
                // Update user to not require password change on next login
                user.SetShouldChangePasswordOnNextLogin(false);
                await UserManager.UpdateAsync(user);
    
                // Get tenant information for email
                var tenant = await _tenantRepository.FindAsync(new Guid(TenantId));
                if (tenant == null)
                {
                    _logger.LogWarning("Tenant not found for TenantId: {TenantId}", TenantId);
                    return BadRequest("Tenant not found");
                }
    
                var tenantHostName = tenant.ExtraProperties
                                    .FirstOrDefault(kvp => kvp.Key.Equals("tenantHostName", StringComparison.OrdinalIgnoreCase))
                                    .Value?.ToString();
    
                // Send confirmation email after successful password change
                _logger.LogInformation("Preparing to send password change confirmation email to {Email} for user {Username}", user.Email, user.UserName);
    
                var emailModel = new EmailNotificationDto
                {
                    MailTo = user.Email,
                    MailSubject = EmailNotificationConstants.EmailTypes.ChangePassword,
                    IsBodyHtml = true,
                    Username = user.UserName,
                    Name = user.Name,
                    TenantName = tenant.Name,
                    TimezoneId = TimezoneId  // Use timezone from client
                };
    
                try
                {
                    if (_emailService == null)
                    {
                        _logger.LogWarning("Email service is not available. Skipping email notification.");
                    }
                    else
                    {
                        _logger.LogDebug("Calling email service to send notification...");
                        var sendTask = _emailService.EmailNotification(emailModel);
    
                        if (await Task.WhenAny(sendTask, Task.Delay(TimeSpan.FromSeconds(30))) == sendTask)
                        {
                            await sendTask; 
                            _logger.LogInformation("Password change email successfully sent to {Email}", user.Email);
                        }
                        else
                        {
                            _logger.LogWarning("Email sending timed out after 30 seconds for {Email}", user.Email);
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "Failed to send password change email to {Email}. Error: {ErrorMessage}", user.Email, ex.Message);
                }
    
                // Refresh the sign-in
                await _signInManager.RefreshSignInAsync(user);
    
                return RedirectToPage("/Account/Login", new
                {
                    tenantName = tenantHostName,
                    returnUrl = ReturnUrl,
                    returnUrlHash = ReturnUrlHash
                });
            }
    
            // Password change failed - add errors to ModelState
            _logger.LogWarning("Password change failed for user {Username} (ID: {UserId})", user.UserName, user.Id);
            foreach (var error in changePasswordResult.Errors)
            {
                _logger.LogDebug("Password change error: {ErrorCode} - {ErrorDescription}", error.Code, error.Description);
                ModelState.AddModelError(string.Empty, error.Description);
            }
            
            return Page();
        }
    }
    

    }

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Found it. Your OnPostAsync calls _userManager.ChangePasswordAsync(...) without calling IdentityOptions.SetAsync() first, so ASP.NET Core Identity validates the password against the framework default PasswordOptions (length=6, requires digit/lower/upper/non-alphanumeric) instead of the values from AbpSettings. That's exactly why the page shows the old "6 characters" errors no matter what you save.

    IdentityOptions.SetAsync() reads the current Abp.Identity.Password.* settings (respecting tenant scope) and pushes them into IdentityOptions.Password, so UserManager.ChangePasswordAsync validates against the values you saved. Inject IOptions<IdentityOptions> and call SetAsync() inside the CurrentTenant.Change scope, before ChangePasswordAsync:

    private readonly IOptions<IdentityOptions> _identityOptions;
    
    public ChangePasswordModel(
        UserManager<IdentityUser> userManager,
        SignInManager<IdentityUser> signInManager,
        IIdentityUserRepository identityUserRepository,
        IConfiguration configuration,
        ITenantRepository tenantRepository,
        IEmailNotificationAppService emailService,
        ILogger<ChangePasswordModel> logger,
        IOptions<IdentityOptions> identityOptions)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _identityUserRepository = identityUserRepository;
        _configuration = configuration;
        _tenantRepository = tenantRepository;
        _emailService = emailService;
        _logger = logger;
        _identityOptions = identityOptions;
    }
    
    public async Task<IActionResult> OnPostAsync()
    {
        using (CurrentTenant.Change(new Guid(TenantId)))
        {
            await _identityOptions.SetAsync();   // <-- add this
    
            var user = await _identityUserRepository.FindAsync(UserId);
            ...
            var changePasswordResult = await _userManager.ChangePasswordAsync(user, CurrentPassword, NewPassword);
            ...
        }
    }
    

    One unrelated note while we're looking at this page: OnGet/OnPost bind UserId and TenantId from query string with no ownership check, so a logged-in user could change any other user's password by editing the URL. You'll likely want to derive them from the current cookie / principal instead, the way ABP's built-in ChangePassword page does.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 16, 2026, 14:50
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.