Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing custom two factor authentication, why does GetTwoFactorAuthenticationUserAsync() returned a null User?

Tags:

c#

asp.net-mvc

namespace weblogint2.Areas.Identity.Data
{
    public partial class AuthSignInManager<TUser> : SignInManager<ApplicationUser> where TUser : class
    {
        private readonly UserManager<ApplicationUser> _userManager;
        private readonly weblogint2Context _db;
        private readonly IHttpContextAccessor _contextAccessor;
        IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
     
        public AuthSignInManager(
            UserManager<ApplicationUser> userManager,
            IHttpContextAccessor contextAccessor,
            IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
            IOptions<IdentityOptions> optionsAccessor,
            ILogger<SignInManager<ApplicationUser>> logger,
            weblogint2Context dbContext,
            IAuthenticationSchemeProvider schemes,
            IUserConfirmation<ApplicationUser> confirmation)
                : base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemes, confirmation)
        {
            _userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
            _contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
            _db = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
            _claimsFactory = claimsFactory;
        }

       public override async Task<SignInResult> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout)
        {
            // WHAT AM I MISSING HERE?

            if (userName.ToLower() == "[email protected]" && password.ToLower() == "111111111@")
            {
                return await Task.FromResult(SignInResult.TwoFactorRequired);
            }
            else
            if (userName.ToLower() == "[email protected]" && password.ToLower() == "111111111@")
            {
                return await Task.FromResult(SignInResult.Success);
            }
            else
            {
                return await Task.FromResult(SignInResult.Failed);
            }
        }
      
        public override async Task<SignInResult> TwoFactorAuthenticatorSignInAsync(string code, bool isPersistent, bool rememberClient)
        {
            var results = Task.FromResult(SignInResult.Failed);
            string GeneratedCode = "124578";
            if (string.IsNullOrEmpty(code))
                return await Task.FromResult(SignInResult.Failed);
            else if(string.Equals(code,GeneratedCode))
            {

                return await Task.FromResult(SignInResult.Success);
            }
            else
            {
                return await Task.FromResult(SignInResult.Failed);
            }
        }
    }
}

At Login Page

var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);

if (result.Succeeded)
{
    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, Input.Email)
    };
}
if (result.RequiresTwoFactor)
{
    // control is commine here 
    var currentUser = await _userManager.FindByEmailAsync(Input.Email);

    return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl });//, RememberMe = Input.RememberMe}) ;
}

At Loginwith2f page

public async Task<IActionResult> OnGetAsync(bool rememberMe, string returnUrl = null)
{
    // Ensure the user has gone through the username & password screen first
    var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();

    if (user == null)
    {
        throw new InvalidOperationException($"Unable to load two-factor authentication user.");

       // NULL HAPPENS HERE
    }

    ReturnUrl = returnUrl;
    RememberMe = rememberMe;

    return Page();
}

ApplicationUser class

public class ApplicationUser : IdentityUser
{
    [PersonalData]
    
    [Column(TypeName ="nvarchar(100)")]
    public string FirstName { get; set;     }

    [PersonalData]
    [Column(TypeName = "nvarchar(100)")]
    public string LastName { get; set;    }

    [PersonalData]
    [Column(TypeName = "bit")]
    public bool IsSystemAdministrator { get; set; }

}

At Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<SignInManager<ApplicationUser>, AuthSignInManager<ApplicationUser>>();
}

've implemented custom AuthSignInManager class when page is redirected to LoginWith2fa. I get null at await _signInManager.GetTwoFactorAuthenticationUserAsync();

like image 585
hayatSikandar Avatar asked Jan 20 '26 20:01

hayatSikandar


1 Answers

TLDR

You're overwriting SignInManager<T> however the functionality you are trying to replace are interfaces themselves. You can replaced the user validation (via IUserValidator) and claims creation (via IClaimsTransformation) logic with their specific interfaces allowing the SignInManager<T> to function as normal.

Explanation

There are a few issues with your code, here are two of the most relevant:

  1. Your AuthSignInManager<TUser> never actually signs the user in. Typically the act of signing a user in done via one of the AuthenticationHttpContextExtensions (example: HttpContext.SignInAsync(...))
  2. The code at your Login page is creating Claims but never associating them to the user. This is typically handled within IClaimsTransformation.

Based on what it seems from your code you are trying to validate user credentials (username and password), append additional claims when credentials are valid, then conditionally continue with the two step verification process.

ASP.NET Core Identity offers ability to easily implement custom code for specifically these tasks. Here are the interfaces needed to implement:

  1. IUserValidator - Remove your PasswordSignInAsync() implementation from AuthSignInManager and instead implement a custom instance of IUserValidator to validate user credentials.
  2. IClaimsTransformation - Remove the code to create claims in your Login page and instead place it within an implementation of IClaimsTransformation.
  3. Register your custom implementations of IUserValidator and IClaimsTransformation within the ConfigureServices() within Startup.cs. Also remove the registration of your AuthSignInManager<ApplicationUser> from ConfigureServices().
like image 146
Jesse Johnson Avatar answered Jan 23 '26 10:01

Jesse Johnson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!