5

I am using the default Login method generated by ASP.NET MVC and wanted to change it so that it will redirect to a specified View based on the user's role. I have checked that the user is in that role. I made the redirect inside the SignInStatus success block with no success.

I use the User.IsInRole() in other blocks of code and works fine. I think the user is not fully logged in when the if statements are executed. I think this is the case, but I am not sure what work around I can implement.

Here is my code below.

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateCustomAntiForgeryTokenAttribute]
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: false);
    switch (result)
    {
        case SignInStatus.Success:
            if (User.IsInRole("Customer"))
            {
                return RedirectToAction("Customer", "Home");
            }
            else if (User.IsInRole("Requestor"))
            {
                return RedirectToAction("Caterer", "Home");
            }
            else if (User.IsInRole("Admin"))
            {
                return RedirectToAction("Admin", "Home");
            }
            else
            {
                return RedirectToLocal(returnUrl);
            }
        case SignInStatus.LockedOut:
            return View("Lockout");
        case SignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
        case SignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Invalid login attempt.");
            return View(model);
    }
}
Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47

4 Answers4

4

Thanks to @stephen.vakil's link I managed to get it to work by changing the block of code inside the SignInStatus.Success case.

            case SignInStatus.Success:
                var user = await UserManager.FindAsync(model.Email, model.Password);
                var roles = await UserManager.GetRolesAsync(user.Id);

                if (roles.Contains("Customer"))
                {
                    return RedirectToAction("Customer", "Home");
                }
                else if (roles.Contains("Requestor"))
                {
                    return RedirectToAction("Caterer", "Home");
                }
                else if (roles.Contains("Admin"))
                {
                    return RedirectToAction("Admin", "Home");
                }
                else
                {
                    return RedirectToLocal(returnUrl);
                }
                ......
1

You are correct. The User object you're referencing here gets set by the "Authentication" step in the ASP.NET pipeline. For more information about this, check out Lifecycle of an ASP.NET 5 Application

PasswordSignInAsync only validates your user and sets up the authentication cookie for future requests. It doesn't affect the User object, which will still represent an unauthenticated state as fed through the pipeline.

A simple way to achieve what you want is to have your Login method redirect to another action (something like RedirectUser) which then performs Role-based routing. This method will have full access to the authenticated User object and the IsInRole method.

Or, you could implement your own User.IsInRole method that queries your DB directly.

SlightlyMoist
  • 882
  • 5
  • 7
1
[HttpPost]
public async Task<IActionResult> SignIn([FromForm]LoginDto userDto, string returnUrl)
{
    if (ModelState.IsValid)
    {
        //var googlereCaptcha = _googlereCaptchaService.ResponceVerify(userDto.ReCaptchaToken);
        //if (!googlereCaptcha.Result.success && googlereCaptcha.Result.score <= 0.5)
        //{
        //    TempData["LoginSuccessMsg"] = "You are not Human.";
        //    return await Task.Run(() => View("SignIn"));
        //}

        var signedUser = await userManager.FindByEmailAsync(userDto.Email);
        var result = await signInManager.PasswordSignInAsync(signedUser.Email, userDto.Password, userDto.RememberMe, lockoutOnFailure: false);

        if (result.Succeeded)
        {
            if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
            {
                return LocalRedirect(returnUrl);
            }
            else
            {
                var roles = await userManager.GetRolesAsync(signedUser);
                if (roles.Contains("Super Admin"))
                    return RedirectToAction("Dashboard", "User");
                if (roles.Contains("Data Entry Operator"))
                    return RedirectToAction("BusinessList", "Business");
                if (roles.Contains("Business Admin"))
                    return RedirectToAction("MyBusiness", "Business");
            }
        }

        ModelState.AddModelError(string.Empty, "Invalid Login Attempt");
    }

    return await Task.Run(() => View(userDto));
}
Ramil Aliyev 007
  • 4,437
  • 2
  • 31
  • 47
0

If you Want to user default login of asp.net Identity then you should get role like this after success result and then redirect will fix this issue

       var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:

                var userId = SignInManager.AuthenticationManager.AuthenticationResponseGrant.Identity.GetUserId();
                if (UserManager.IsInRole(userId, "Super Admin"))
                {
                    return RedirectToAction("Index", "DashBoard");
                }}
Hamza Shafiq
  • 69
  • 1
  • 13