Refactoring User Authentication in ABP Framework

a、Core Layer - Authorizasion.Users.UserStore.cs

public class UserStore : AbpUserStore<Role, User>
{
    private readonly IRepository<User, long> _userRepository;

    public UserStore(
        IUnitOfWorkManager unitOfWorkManager,
        IRepository<User, long> userRepository,
        IRepository<Role> roleRepository,
        IAsyncQueryableExecuter asyncQueryableExecuter,
        IRepository<UserRole, long> userRoleRepository,
        IRepository<UserLogin, long> userLoginRepository,
        IRepository<UserClaim, long> userClaimRepository,
        IRepository<UserPermissionSetting, long> userPermissionSettingRepository,
        IRepository<UserOrganizationUnit, long> userOrganizationUnitRepository,
        IRepository<OrganizationUnitRole, long> organizationUnitRoleRepository)
        : base(
            unitOfWorkManager,
            userRepository,
            roleRepository,
            asyncQueryableExecuter,
            userRoleRepository,
            userLoginRepository,
            userClaimRepository,
            userPermissionSettingRepository,
            userOrganizationUnitRepository,
            organizationUnitRoleRepository)
    {
        _userRepository = userRepository;
    }

    /// <summary>
    /// Retrieves a user by their account identifier
    /// </summary>
    /// <param name="account">The account identifier</param>
    /// <returns>The matching user or null</returns>
    public virtual async Task<User> FindByAccountAsync(string account)
    {
        account = account.ToLower();
        return await _userRepository.FirstOrDefaultAsync(
            user => user.UserName.ToLower() == account
        );
    }
}

b、Core Layer - Authorization.LogInManager.cs

public class LogInManager : AbpLogInManager<Tenant, Role, User>
{
    private readonly UserStore _userStore;
    private readonly AbpUserManager<Role, User> _userManager;

    public LogInManager(
        UserManager userManager,
        IMultiTenancyConfig multiTenancyConfig,
        IRepository<Tenant> tenantRepository,
        IUnitOfWorkManager unitOfWorkManager,
        ISettingManager settingManager,
        IRepository<UserLoginAttempt, long> userLoginAttemptRepository,
        IUserManagementConfig userManagementConfig,
        IIocResolver iocResolver,
        IPasswordHasher<User> passwordHasher,
        RoleManager roleManager,
        UserClaimsPrincipalFactory claimsPrincipalFactory,
        UserStore userStore)
        : base(
              userManager,
              multiTenancyConfig,
              tenantRepository,
              unitOfWorkManager,
              settingManager,
              userLoginAttemptRepository,
              userManagementConfig,
              iocResolver,
              passwordHasher,
              roleManager,
              claimsPrincipalFactory)
    {
        _userStore = userStore;
        _userManager = userManager;
    }

    /// <summary>
    /// Custom login implementation
    /// </summary>
    /// <param name="account">Username, phone number, or ID number</param>
    /// <param name="password">Plain text password</param>
    /// <returns>Login result</returns>
    [UnitOfWork]
    public virtual async Task<AbpLoginResult<Tenant, User>> LoginCustomAsync(string account, string password)
    {
        var result = await LoginCustomAsyncInternal(account, password);

        // Record login attempt
        await SaveLoginAttemptAsync(result, null, account);
        return result;
    }

    protected virtual async Task<AbpLoginResult<Tenant, User>> LoginCustomAsyncInternal(string account, string password)
    {
        if (account.IsNullOrEmpty() || password.IsNullOrEmpty())
        {
            throw new ArgumentException("account or password");
        }

        // Retrieve default tenant if multi-tenancy is disabled
        Tenant tenant = await GetDefaultTenantAsync();

        int? tenantId = tenant?.Id;
        using (UnitOfWorkManager.Current.SetTenantId(tenantId))
        {
            // Fetch user based on account
            var user = await _userStore.FindByAccountAsync(account);
            if (user == null)
            {
                return new AbpLoginResult<Tenant, User>(AbpLoginResultType.UnknownExternalLogin, tenant);
            }

            // Validate password
            var verificationResult = _userManager.PasswordHasher.VerifyHashedPassword(user, user.Password, password);
            if (verificationResult != PasswordVerificationResult.Success)
            {
                if (await TryLockOutAsync(tenantId, user.Id))
                {
                    return new AbpLoginResult<Tenant, User>(AbpLoginResultType.LockedOut, tenant, user);
                }

                return new AbpLoginResult<Tenant, User>(AbpLoginResultType.InvalidPassword, tenant, user);
            }

            // Reset failed login attempts
            await _userManager.ResetAccessFailedCountAsync(user);

            // Generate successful login result
            return await CreateLoginResultAsync(user, tenant);
        }
    }
}

c、Web.Core Layer

Add method:

/// <summary>
/// Performs custom login
/// </summary>
/// <param name="userName">Account, ID, or phone number</param>
/// <param name="password">Plain text password</param>
/// <returns>Login result</returns>
private async Task<AbpLoginResult<Tenant, User>> GetCustomLoginResultAsync(string userName, string password)
{
    var loginResult = await _logInManager.LoginCustomAsync(userName, password);

    switch (loginResult.Result)
    {
        case AbpLoginResultType.Success:
            return loginResult;
        default:
            throw _abpLoginResultTypeHelper.CreateExceptionForFailedLoginAttempt(loginResult.Result, userName, null);
    }
}

Refactoerd method:

[HttpPost]
public async Task<AuthenticateResultModel> Authenticate([FromBody] AuthenticateModel model)
{
    //var loginResult = await GetLoginResultAsync(
    //    model.UserNameOrEmailAddress,
    //    model.Password,
    //    GetTenancyNameOrNull()
    //);

    // Custom login implementation
    var loginResult = await GetCustomLoginResultAsync(
        model.UserNameOrEmailAddress,
        model.Password
    );

    var accessToken = CreateAccessToken(CreateJwtClaims(loginResult.Identity));

    return new AuthenticateResultModel
    {
        AccessToken = accessToken,
        EncryptedAccessToken = GetEncryptedAccessToken(accessToken),
        ExpireInSeconds = (int)_configuration.Expiration.TotalSeconds,
        UserId = loginResult.User.Id
    };
}

Tags: ABP Authentication refactoring login Security

Posted on Fri, 08 May 2026 19:39:55 +0000 by brunosdiniz