Are you talking about the auto login once the session is expired
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<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager, IIdentityUserRepository identityUserRepository,
IConfiguration configuration, ITenantRepository tenantRepository, IEmailNotificationAppService emailService, ILogger<ChangePasswordModel> 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<IActionResult> 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();
}
}
}
we have to redirect to Angular UI / SPA scenario, and it returns 302 and empty data. but after hard refresh it returns the data and response code is 200
Even clearing the cache, it not working I have added this piece of configuration in Host module class it working. is it ok add
// Configure password requirements
Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = 8;
});