User roles are zero after login in MVC6? - asp.net-core

I am using the default site template that comes in visual studio 2015. I have added some roles and assigned roles to the user. When a used signs in, the roles are zero. What do I need to do to get the roles working?
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (ModelState.IsValid)
{
var signInStatus = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
switch (signInStatus)
{
case SignInStatus.Success:
var user = await GetCurrentUserAsync();
return RedirectToLocal(returnUrl);
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}

Looking at your code, it looks like you're missing any check for a role.
I was having this similar problem and after digging into the code I noticed that GetCurrentUserAsync() was not returning a valid user object and was intact returning a null object. Context.User.GetUserId() is the problem and isn't returning a user id to pass on to the UserManager.
Until this is resolved I'm using the following:
if (result.Succeeded)
{
var user = await UserManager.FindByEmailAsync(model.Email);
if (await UserManager.IsInRoleAsync(user, Roles.Admin))
{
return RedirectToAction("Index", "Admin");
}
return RedirectToLocal(returnUrl);
}
While this isn't ideal it does work

This works for me...does not redirect correct for Invalid username or password 4 me yet...will check...
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
if (ModelState.IsValid)
{
var result = await SignInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);
if (result.Succeeded)
{
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
this is for MVC 6, (VS 2015 RC)...u can find it all here : https://github.com/aspnet/Identity/tree/dev/samples/IdentitySample.Mvc

Related

Redirect To Action Not Working after login ASP.NET Core Identity (.Net 5)

Even after a successful sign-in it does not redirect to action but stays on the login page.
I have put breakpoints but have not been able to know what exactly is wrong with my code.
Even if the condition is true the code is not executed.
Here is my code:
[HttpPost]
public async Task<ActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _context.Users.SingleOrDefaultAsync(u => u.UserName == model.UserName);
if (user != null)
{
var result = await _signInManager.PasswordSignInAsync(user, model.Password, true, false);
if (result.Succeeded)
{
var role = await _userManager.GetRolesAsync(user);
if (role.Contains("Client"))
{
return RedirectToAction("Index", "MyDashboard");
}
if (role.Contains("Admin"))
{
return RedirectToAction("Index", "Admin");
}
}
}
else
{
ModelState.AddModelError("", "Invalid login attempt");
return View(model);
}
}
return View();
}
I got the solution myself. Actually, I was missing Authentication middleware in the startup class. After adding app.UseAuthentication() ,it works fine. Thanks.

Force 2 Factor Authentication for new users when they login for the first time .NET Core

I am trying to configure the 2FA when users log in for the first time in .net core. So I added an if condition for checking if 2FA is not enabled then redirecting to creating MFA, however, a major flaw here is that the users can change the URL link on the browser to skip 2FA creation, how can I avoid this? Below are my Account Controller Codes:
Login Controller Methods
[HttpGet]
[AllowAnonymous]
public IActionResult Login(string? returnurl = null)
{
ViewData["ReturnUrl"] = returnurl;
return View();
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login(LoginViewModel model, string? returnurl = null)
{
ViewData["ReturnUrl"] = returnurl;
returnurl ??= Url.Content("~/");
if (ModelState.IsValid)
{
var user = await _userManager.FindByNameAsync(model.UserName);
var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, lockoutOnFailure: true);
if (result.Succeeded)
{
if (user.TwoFactorEnabled==false)
{
return RedirectToAction(nameof(EnableAuthenticator), new { returnurl, model.RememberMe });
}
return LocalRedirect(returnurl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(VerifyAuthenticatorCode), new { returnurl, model.RememberMe });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
return View(model);
}
Enable 2FA Controller Methods
[HttpGet]
public async Task<IActionResult> EnableAuthenticator()
{
string AuthenticatorUriFormat = "MY-OTP-SECRET-HERE";
var user = await _userManager.GetUserAsync(User);
await _userManager.ResetAuthenticatorKeyAsync(user);
var token = await _userManager.GetAuthenticatorKeyAsync(user);
string AuthenticatorUri = string.Format(AuthenticatorUriFormat, _urlEncoder.Encode("My-App-Name-Here"),
_urlEncoder.Encode(user.Email), token);
var model = new MFAViewModel() { Token = token, QRCodeUrl = AuthenticatorUri };
return View(model);
}
[HttpPost]
public async Task<IActionResult> EnableAuthenticator(MFAViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.GetUserAsync(User);
var succeeded = await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, model.Code);
if (succeeded)
{
await _userManager.SetTwoFactorEnabledAsync(user, true);
}
else
{
ModelState.AddModelError("Verify", "Your two factor auth code could not be validated.");
return View(model);
}
}
return RedirectToAction(nameof(AuthenticatorConfirmation));
}
As mentioned in my comment, you could consider configuring the application use Claims-based authorization, after user login with 2FA successfully, you could add a claim store the 2FA login result and add it to the login user. After that, in your application, create a policy which requires the claim, and add the Authorize attribute on each controller.
Besides, you could also add the user's claims after 2FA , then create a custom middleware/Authorize attribute to validate each request and check whether the current user contains the claims or not. You can refer to the following links: Custom Authorization attributes and How To Override Attribute Class To Do Custom Authorization In .NET Core.

User.Identity.IsAuthenticated is false although SignInManager.PasswordSignInAsync() succeeds

I did find similar questions but none of the provided answers helped me.
I did follow a tutorial to add Identity to my ASP.net Core 2.2 project (https://retifrav.github.io/blog/2018/03/20/csharp-dotnet-core-identity-mysql/)
Even though SignInManager.PasswordSignInAsync() succeeds, both User.Identity.IsAuthenticated and SignInManager.IsSignedIn(User) are false in the _Layout view.
_Layout.cshtml:
#using Microsoft.AspNetCore.Identity
#inject SignInManager<MySiteUser> SignInManager
#inject UserManager<MysiteUser> UserManager
......................
<div>
#if (SignInManager.IsSignedIn(User))
{
<div>Hello #UserManager.GetUserName(User)!</div>
}
#if (User.Identity.IsAuthenticated)
{
<div>User is authenticated </div>
}
</div>
In Startup.CS, in ConfigureServices I have:
services.AddIdentity<MySiteUSer, MySiteRole>().AddEntityFrameworkStores<IdentityContext>().AddDefaultTokenProviders();
services.Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.User.RequireUniqueEmail = true;
});
services.ConfigureApplicationCookie(options => options.LoginPath = "/Account/Login");
services.AddMvc();
In Startup.CS, in Configure() I have:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IServiceProvider services)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication(); ;
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
In AccountController I have:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
var user = await _signInManager.UserManager.FindByNameAsync(model.Username);
var userPrincipal = await _signInManager.CreateUserPrincipalAsync(user);
var identity = userPrincipal.Identity;
if(identity.IsAuthenticated)
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
MySiteUser and MySiteRole just override default Identity classes
public class MySiteUser : IdentityUser<int>
{
}
public class MySiteRole : IdentityRole<int>
{
}
Edit:
Because all replies are about the Controller, before this one I used the following code in AccountController
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
The result was the same.
I've just find a solution, although I don't understand why it works. I've checked Chrome Developer Tools to see if the authentication cookie is set, and it wasn't.
After I deleted all cookies for the site, the app set the cookie and all works well.
I also tested the simpler AccountController and that works fine, too:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var result = await _signInManager
.PasswordSignInAsync(model.Username, model.Password,
Model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
return RedirectToAction("Index", "Home");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return View(model);
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
You don't need to check if(identity.IsAuthenticated) before redirecting user to home page. If the code reaches the (result.Succeeded) block, it means user is authenticated. if(identity.IsAuthenticated) will be true only at the end of the request, meaning you have returned an HttpResponse (or something) that will create a cookie on client side.
So the solution is to remove the condition inside the Login action.
/*if(identity.IsAuthenticated) <===== remove this */
return RedirectToAction("Index", "Home");
EDITS: Don't forget that when you use RedirectToAction, it's still a same request so the cookie will not be created yet. Instead, create and use a temporary success view as I usually do.
/*if(identity.IsAuthenticated) <===== remove this */
return RedirectToAction("Index", "Home"); <===== remove this */
/*put url in viewbag or use returnUrl variable*/
#ViewBag.ReturnUrl = Url.Action("index","home"/*,null,Request.Url.Scheme*/);
return View("LoginSuccess");
and here is what to put in your LoginSuccess.cshtml view
<h2>Authenticated. Wait 2 seconds or click continue</h2>
<p><a href="#ViewBag.ReturnUrl">
Continue</a></p>
<script>
setTimeout(function () {
window.location = "#ViewBag.ReturnUrl";
}, 2000)
</script>
PS: You may need to use partial view to avoid conflict with your layout page headers... return PartialView("LoginSuccess");
I believe I have stumbled on this answer to this. First try this in IE, it may work there. Apparently Chrome is quite fussy regarding the cookie that is used for the Authentication unless you are talking to it in HTTPS. If you are in development and using HTTP and using Chrome the IsSignedIn(User) may fail. This is unbelievably annoying and quite a productivity sink. HTH

ASP.NET Core - Registering new Users

I'm adding the functionality of adding new users to my project following this project:
http://www.c-sharpcorner.com/article/asp-net-core-mvc-authentication-and-role-based-authorization-with-asp-net-core/
However, I encounter a problem when I try to Add a new user:
On the Post Method of the UserController:
[HttpPost]
public async Task<IActionResult> AddUser(UserViewModel model)
{
if (ModelState.IsValid)
{
ApplicationUser user = new ApplicationUser
{
Name = model.Name,
UserName = model.UserName,
Email = model.Email
};
IdentityResult result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
ApplicationRole applicationRole = await roleManager.FindByIdAsync(model.ApplicationRoleId);
if (applicationRole != null)
{
IdentityResult roleResult = await userManager.AddToRoleAsync(user, applicationRole.Name);
if (roleResult.Succeeded)
{
return RedirectToAction("Index");
}
}
}
}
return View(model);
I'm getting an error on this line:
IdentityResult result = await userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
When I put a break here, I see that result is null.
I'm trying to solve it on my own but no luck so far.
Any ideas? Thanks in advance for any help.
Regards,

Url Rewriting in MVC4

I'm processing Login and Register on the same page. When I click Register button I process in different Controller but I don't want to my URL.
Example: When i request
http:localhost:1853/Account/RegisterLogin
I want to when I post if Model is invalid my URL still not change.
// GET: /Account/RegisterLogin
[AllowAnonymous]
public ActionResult RegisterLogin(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
ViewData["RegisterModel"] = new RegisterModel();
ViewData["LoginModel"] = new LoginModel();
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Gender = model.Gender, FirstName = model.FirstName, LastName = model.LastName, BirthDate = model.BirthDate, Email = model.Email }, false);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
ViewData["RegisterModel"] = model;
ViewData["LoginModel"] = new LoginModel();
return View("RegisterLogin");
}
Thanks you for your help!
I advise you to post your model to the action in the same controller with the same name and add a flag to your model showing if user login or register, and according to a value of the flag do appropriate actions. It allows you to save current model state value and stay on the URL
//GET /Account/Register
[AllowAnonymous]
public ActionResult RegisterLogin(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View(new RegisterLoginModel() { ReturnUrl = returnUrl, IsLogin = false });
}
//POST /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult RegisterLogin(RegisterLoginModel model)
{
if (!ModelState.IsValid)
{
if (!model.IsLogin)
{
// Attempt to register the user
try
{
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Gender = model.Gender, FirstName = model.FirstName, LastName = model.LastName, BirthDate = model.BirthDate, Email = model.Email }, false);
WebSecurity.Login(model.UserName, model.Password);
return RedirectToAction("Index", "Home");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
else
{
//do something
}
}
return View(model);
}