简体   繁体   中英

Auto-redirect not working after user authentication

My team and I are starting up a new website project in ASP .NET 5 and I'm trying to set up the basis of our user authentication and authorization policy.

I am currently toying with cookie-based authentication for returning users, so I've added the following to the Configure method of my Startup.cs:

        // Redirect to login page if user is not logged in.
        app.UseCookieAuthentication(options =>
        {
            options.AutomaticAuthentication = true;
            options.ExpireTimeSpan = new System.TimeSpan(0, 1, 0);
            options.SlidingExpiration = true;
            options.LoginPath = "/Login";
            options.ReturnUrlParameter = "ReturnUrl";
        });

The authentication service is also added to the ConfigureServices method.

My concern is with the LoginPath parameter: while automatic redirection to the Login page works correctly when my middleware/filter (I tried both) returns a 401 code, automatic redirection back from the login page to the one that was originally requested doesn't work: the browser remains on the login page even after a successful login.

I strongly suspect the problem lies somewhere within my controller. Here is its (slightly simplified) source code:

[Route("[controller]")]
[AllowAnonymous]
public class LoginController : Controller
{
    private static readonly string AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;

    public IActionResult Index()
    {
        return View();
    }

    [HttpPost]
    public async System.Threading.Tasks.Task<ActionResult> Post(LoginInfo infos)
    {
        if (ValidateLogin(infos))
        {
            await Context.Authentication.SignInAsync(AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
            {
                new Claim(ClaimTypes.Role, "Admin")
            }, 
            AuthenticationScheme)));
        }

        return View("Index");
    }

    private static bool ValidateLogin(LoginInfo infos)
    {
        return infos.Username == "abc" && infos.Password == "def";
    }
}

(bits of code removed for clarity, eg displaying an error message if the username/pwd combination is rejected)

The view is very simple and contains a simple form providing a couple of edit boxes and POST-ing the user/password to the controller (again, error message removed for clarity):

@using (Html.BeginForm("Post", "Login", FormMethod.Post))
{
    <div>
        <h2>Login Page</h2>
        <p>
            @Html.Label("Username")
            @Html.Editor("Username")
        </p>

        <p>
            @Html.Label("Password")
            @Html.Password("Password")
        </p>

        <p>
            <input type="submit" value="Attempt login" />
        </p>
    </div>
}

I've been able to check that user sign-in works correctly by fetching user identity and claims in a separate controller. Initial redirection to the login page also works correctly (ie I get redirected to '/Login' when I try to access protected parts of the website without logging in), and the redirected URL correctly contains a ReturnUrl query parameter. I have also tried accessing the Referer URL while handling the Post action and managed to forcefully trigger redirection by manually returning a RedirectResult from the controller, so the contents of the ReturnUrl themselves must be good too.

So... can anyone help me understand why redirection to the return URL is not performed automatically?

edit: according to the documentation of CookieAuthenticationOptions.LoginPath:

Summary: The LoginPath property informs the middleware that it should change an outgoing 401 Unauthorized status code into a 302 redirection onto the given login path. The current url which generated the 401 is added to the LoginPath as a query string parameter named by the ReturnUrlParameter. Once a request to the LoginPath grants a new SignIn identity, the ReturnUrlParameter value is used to redirect the browser back to the url which caused the original unauthorized status code. If the LoginPath is null or empty, the middleware will not look for 401 Unauthorized status codes, and it will not redirect automatically when a login occurs.

The part in bold is the one I'm trying to leverage... redirecting to a static page (ie manually returning a Redirect() to some hardcoded page) is not what I'm trying to accomplish here.

The problem is you always return to the Index view after a successful login:

[HttpPost]
public async System.Threading.Tasks.Task<ActionResult> Post(LoginInfo infos)
{
        if (ValidateLogin(infos))
        {
            await Context.Authentication.SignInAsync(AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
            {
                new Claim(ClaimTypes.Role, "Admin")
            }, 
            AuthenticationScheme)));
        }

        return View("Index"); //the problem is here
}

Try replacing return View("Index"); with a view that you want to redirect the user to (maybe on another controller). You could try RedirectToAction : Redirect to Action in another controller . Something like:

return RedirectToAction("Index", "Home");

In your case, if you rely on the framework to do redirection for you, try:

[HttpPost]
public async System.Threading.Tasks.Task Post(LoginInfo infos)
{
        if (ValidateLogin(infos))
        {
            await Context.Authentication.SignInAsync(AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
            {
                new Claim(ClaimTypes.Role, "Admin")
            }, 
            AuthenticationScheme)));
        }
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM