ASP Identity Issue not authenticting - asp.net

So I'm using ASP Identity. I customized the profile with adding isEnabled so that I know if a user is Active or not. This works fine and the database is updated without issue.
So after research, I saw that I need to override "public async override Task PasswordSignInAsync" that I can check for the isEnabled property. This works fine as well because it's passing the proper SigninStatus that I need to the Login Page.
The IdentifyConfig.cs File:
public async override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
var user = UserManager.FindByEmailAsync(userName).Result;
if (user.IsEnabled != false)
{
return await Task.FromResult<SignInStatus>(SignInStatus.Success);
}
return await Task.FromResult<SignInStatus>(SignInStatus.LockedOut);
}
The Login Page:
protected void LogIn(object sender, EventArgs e)
{
if (IsValid)
{
// Validate the user password
var manager = Context.GetOwinContext().GetUserManager<ApplicationUserManager>();
var signinManager = Context.GetOwinContext().GetUserManager<ApplicationSignInManager>();
// This doen't count login failures towards account lockout
// To enable password failures to trigger lockout, change to shouldLockout: true
//var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
var result = signinManager.PasswordSignInAsync(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
switch (result.Result)
{
case SignInStatus.Success:
//IdentityHelper.RedirectToReturnUrl(Request.QueryString["ReturnUrl"], Response);
Response.Redirect("/Account/Dashboard");
break;
case SignInStatus.LockedOut:
Response.Redirect("/Account/Lockout");
break;
case SignInStatus.RequiresVerification:
Response.Redirect(String.Format("/Account/TwoFactorAuthenticationSignIn?ReturnUrl={0}&RememberMe={1}",
Request.QueryString["ReturnUrl"],
RememberMe.Checked),
true);
break;
case SignInStatus.Failure:
default:
//FailureText.Text = "Invalid login attempt";
//ErrorMessage.Visible = true;
break;
}
}
}
The switch case goes into the right case, but here's the issue. When it goes into the case SignInStatus.Success: it just stays in a loop and keeps presenting the Login Screen as if the user is not authenticated. Now, I'm sure i am missing something on the Override for
public async override Task PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
function, but I thought if Success came back that the user is authorized.
If I uncomment
//var result = signinManager.PasswordSignIn(Email.Text, Password.Text, RememberMe.Checked, shouldLockout: false);
function it works fine, but I can't get the IsEnabled value to check. Please any advice would be appreciated on this issue.

I believe that we have a few issues that are compounding for you, making this process a bit hard to dissect
Your async implementation could cause some thread issues
We want to be sure to handle a null situation on user lookup, for a bad username
We want to be sure that we actually validate the password
We need to call the method necessary to login the user as well, as that is a responsibility of this method
I'm going to assume that you are using the non ASP.NET Core version of things. As such, this modified version should help. I have not been able to 100% validate.
public async override Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
var user = await UserManager.FindByEmailAsync(userName);
//User not found
if (user == null)
return SignInStatus.Failure;
//User not enabled
if (user.IsEnabled != false)
return SignInStatus.Failure;
//Now, validate that the password was correct
var isValidPassword = await UserManager.CheckPasswordAsync(user, password);
if (!isValidPassword)
return SignInStatus.Failure;
//Now we know they are valid and enabled, login and return
await base.SignInAsync(user, isPersistent, false);
return SignInStatus.Success;
}
This above code SHOULD work for you, without issue. The only question is on the type that is used to pass into the CheckPassword asyc, I don't have a project ready to validate. But this should get you there.

Related

User login using Email or Username gives null if user not found in ASP.NET Core MVC

I am using the default identity pages with some modifications, in the login page I included the username for the user to login. It works perfectly, the user now can login by both the email and username, but when the users enters false info, a null exception appears instead of showing
Invalid login attempt
Code:
if (ModelState.IsValid)
{
//Check if user entered email or username in the Input.Email property
var user = await _userManager.FindByNameAsync(Input.Email) ?? await _userManager.FindByEmailAsync(Input.Email);
// user is null if not exist? error
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(user, Input.Password, Input.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
//some code
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
I get why the error happens, but don't know what the proper way to solve it.
Originally the signInManager checks the user input not the actual user, so if the input is not found it will not be succeeded, how can I do it the same old way?
when the users enters false info, a null exception appears
In source code of PasswordSignInAsync(TUser, String, Boolean, Boolean) method, we can find it will throw NullException error if user is null.
public virtual async Task<SignInResult> PasswordSignInAsync(TUser user, string password,
bool isPersistent, bool lockoutOnFailure)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var attempt = await CheckPasswordSignInAsync(user, password, lockoutOnFailure);
return attempt.Succeeded
? await SignInOrTwoFactorAsync(user, isPersistent)
: attempt;
}
how can I do it the same old way?
You can modify the code as below to check if user is null, and set and display "Invalid login attempt." error message.
var user = await _userManager.FindByNameAsync(Input.Email) ?? await _userManager.FindByEmailAsync(Input.Email);
if (user == null)
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
var result = await _signInManager.PasswordSignInAsync(user, Input.Password, Input.RememberMe, lockoutOnFailure: true);
//...
//code logic here

Can't configure UserCookie to return correct SignInStatus for two factor Authentication

According to MSDN
Both the local log in and social log in check to see if 2FA is enabled. If 2FA is enabled, the SignInManager logon method returns SignInStatus.RequiresVerification, and the user will be redirected to the SendCode action method, where they will have to enter the code to complete the log in sequence. If the user has RememberMe is set on the users local cookie, the SignInManager will return SignInStatus.Success and they will not have to go through 2FA.
I do want the user to be able to use the remember me feature of the application but I can not figure out how to get the cookie to ditch this setting so that the SignInStatus returns RequiresVerifacation. I'm actually not even 100% sure that the cookie is causing it. All I know is that I have enabled TFA and in AspUsers table I can see that TwoFactorEnabled is set to true but the status is always returning as Success.
Here is the controller where I am not getting what I want
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
According to this MSDN page var result should return SignInStatus.RequiresVerification but it returns Success when returning from the OAuth sign in from google or just a regular sign in. The User has their TwoFactorEnabled set to true in the AspUsers Table which is what result is checking according to docs.
The solution to this problem was actually incredibly simple. You just need to KNOW WHAT YOU'RE DOING. ASP Identity is complex and there are a lot of moving parts. If anyone is coming across this post struggling with ASP.NET Identity I would recommend starting here with the Microsoft video series on customizing ASP Identity. There is a lot of info there. The tutorial most helpful for implementing a Google Authenticator style of TFA with QR codes is here

Creating users with no password using ASP.NET Identity

I have been given the requirement to provide the ability to create users through the UI with no password. I am trying to accomplish this using ASP.NET Identity.
I am able to successfully create a user without a password using the UserManager's Create method:
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert);
}
After the call to the Create method, the test user gets successfully saved into our AspNetUsers table. And when I do not provide a password, the PasswordHash column in our AspNetUsers table is set to NULL.
My issue is, I cannot login as the test user that does not have a password. The following is the method call that we use to validate a user's credentials:
result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
I attempted to login as a test user that has a NULL PasswordHash multiple times. To do this, I do not provide a password in our login form. As a result, a NULL password is passed into the PasswordSignInAsync method. The return value of this method call is always SignInStatus.Failure.
Using ASP.NET Identity, how can I configure my code to correctly authenticate user credentials when the credentials contain a NULL password, and the user in the database contains a NULL PasswordHash? Is such a thing even possible?
Yes you can. ASP.NET Identity Framework is fully customizable. Just override PasswordValidator.ValidateAsync and PasswordHasher.VerifyHashedPassword methods like this:
internal class CustomPasswordValidator: PasswordValidator
{
public override async Task<IdentityResult> ValidateAsync(string item)
{
if (string.IsNullOrEmpty(item)) return IdentityResult.Success;
return await base.ValidateAsync(item);
}
}
internal class CustomPasswordHasher : PasswordHasher
{
public override PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (hashedPassword == null && string.IsNullOrEmpty(providedPassword))
return PasswordVerificationResult.Success;
return base.VerifyHashedPassword(hashedPassword, providedPassword);
}
}
And set them like this:
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
manager.PasswordValidator = new CustomPasswordValidator();
manager.PasswordHasher = new CustomPasswordHasher();
Okay, what you need to do is find the user (AspNetUsers user) using your db context. After you have the user, you can check if their PasswordHash is null.
If yes, then just sign them in using SignInManager.SignIn.
If not, use SignInManager.PasswordSignIn.
example..
//alternatively, you can find the user using Email, Id or some other unique field
var user = db.AspNetUsers.FirstOrDefault(p => p.UserName);
if (user != null)
{
if (user.PasswordHash == null)
await SignInManager.SignInAsync(user, true, true);
else
await SignInManager.PasswordSignInAsync(model.UserName, model.Password,
model.RememberMe, shouldLockout: false);
}
Hope it helps.
I don't think you can validate user without password. As a workaround: Instead of blank password, I'll recommend to use some Dummy/Common password from C# code, both while creating User and while validating credential
When creating user
if (vm.ShouldHavePassword)
{
userManager.Create(userToInsert, vm.Password);
}
else
{
userManager.Create(userToInsert, "someDummy123$");
}
When validating
result = await SignInManager.PasswordSignInAsync(model.UserName, "someDummy123$", model.RememberMe, shouldLockout: false);

How to set up two-factor authentication in ASP.NET MVC 5.2.3 and Katana correctly?

In the code that you get in the ASP.NET MVC 5.2.3 templates with Visual Studio 2015 Community RC, if you run them as they came, and if you register with your email address (and not with an external service provider such as Facebook or Google or Linked In or Twitter), and then if you login into the website by entering your user name and password, it straight-away lets you login and does not trigger two-factor authentication. It just logs you in successfully.
Specifically, the PasswordSignInAsync method on the SignInManager always returns a SignInStatus of Success if you enter your correct user name and password. It never evaluates to SignInStatus.RequiresVerification.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model,
string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout,
// change to shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(
model.Email, model.Password,
model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
// if I sign-in with my correct user name
// and password, the flow-of-control always
// comes here. The SignInStatus never evaluates
// to RequiresVerification
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
// the flow-of-control never reaches here
return RedirectToAction("SendCode",
new
{
ReturnUrl = returnUrl,
RememberMe = model.RememberMe
});
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
This happens even though the default code has got two-factor authentication enabled and set up as indicated by the following snippets of code.
In Startup.ConfigureAuth
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie,
TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(
DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
In ApplicationUserManager.Create, the factory method.
// Register two factor authentication providers. This application uses Phone
// and Emails as a step of receiving a code for verifying the user
// You can write your own provider and plug it in here.
manager.RegisterTwoFactorProvider("Phone Code",
new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Your security code is {0}"
});
manager.RegisterTwoFactorProvider("Email Code",
new EmailTokenProvider<ApplicationUser>
{
Subject = "Security Code",
BodyFormat = "Your security code is {0}"
});
var container = Unity.Container;
manager.EmailService = container.Resolve<EmailService>();
manager.SmsService = container.Resolve<SmsService>();
I've got my EmailService and SmsService set up in a Unity container and they're configured properly.
What else do I need to do to set it up correctly? I have read this article and a few pieces of documentation from the MSDN, and a few forums posts on other websites about setting this up, but I am not very certain if I am missing something.
This guy isn't called / redirected to from anywhere. I guess this is what's missing.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EnableTwoFactorAuthentication()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(),
true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false,
rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
}
It does seem that I am missing the part where I have to specifically have the user call the EnableTwoFactorAuthentication action as there is currently no call to it, but I can't be sure how that should integrate with the rest of the login workflow.
Click f12 from your browser and delete application Cookies

Why does IsInRole always return false?

I am building an MVC5 application with ASP.NET Identity 2.0 and EF 6.1.1.
This is part of my current Login method:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
return View(model);
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: true);
switch (result)
{
case SignInStatus.Success:
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
if (user == null)
break;
if (!UserManager.IsInRole(user.Id, "OfficeUser"))
break; // << I always hit this line - even if the user is an OfficeUser
This works fine until I hit UserManager.IsInRole(). This always returns false.
Somewhere I read that IsInRole() will fail if the corresponding user is not signed in. But since I pass SignInManager.PasswordSignInAsync() I believe this should be fine.
And yes, I have checked the database many times and very carefully ;) My test user and my test role "OfficeUser" are definitely assigned to each other.
Does anybody have an idea?
I had same kind of issue, then I got work it as below. The reason seems to be, the user not completely signin or completed all authentication process in this stage.
case SignInStatus.Success:
{
var user = await UserManager.FindAsync(model.UserName, model.Password);
var roles = await UserManager.GetRolesAsync(user.Id)
if (user == null)
break;
if (roles.Contains("OfficeUser"))
break; // << I always hit this line - even if the user is an OfficeUser

Resources