Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the end user have to log out twice?

I am trying to get IdentityServer4 working inside a new .NET Core 2.1 app (it works perfectly inside a .NET Core 2.0 app). I have tried the following:

1) Download this project, which is the IdentityServer4 application: https://github.com/ghstahl/IdentityServer4-Asp.Net-2.1-Identity-Examples/tree/e0aeeff7e078aa082c8e16029dd2c220acc77d7b

2) Download this project, which is the MVC application using the Identity Server4 application: https://github.com/IdentityServer/IdentityServer4.Samples/tree/dev/Quickstarts/6_AspNetIdentity/src/MvcClient.

3) Add the two projects to the same solution. The MVC project uses the IdentityServer project for authentication; authorisation etc.

I had to make the following changes:

1) Change to the Startup contained in the IdentityServer app (AddIdentityServer now accepts an argument):

services.AddIdentityServer(options =>
{
    options.UserInteraction.LoginUrl = "/Identity/Account/Login";
    options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
})

2) Configure the IdentityServer app to listen on port 5000 and disable SSL on the identity server.

Everything works as expected out of the box, except the logout facility. When I click log out in the MVC application; the following code is called inside the MVC app:

public async Task Logout() 
{ 
    await HttpContext.SignOutAsync("Cookies"); 
    await HttpContext.SignOutAsync("oidc"); 
} 

The user is then redirected to Logout.cshtml in the IdentityServer app. However, they have to click log out again (on the IdentityServer app) in order to actually log out i.e. they click log out in the MVC app (point two), then log out in IdentityServer (point one).

Why does the end user have to log out twice?

like image 228
w0051977 Avatar asked Aug 09 '18 09:08

w0051977


People also ask

What happens at the end of a user session?

The user's session ends and is preserved in computer memory. The station becomes available for log on by the same user or a different user. The user can log on to the same station or another station and continue with their work.

Why does my website keep logging Me Out?

Why does any website keep logging a user out and make to login again? The answer depends on the website. Particularly websites that deal with money, such as banks and e-commerce website need to log users out to prevent unauthorised access. I certainly don’t want my pennies vanishing because someone got hold of my laptop and started playing around.

How do I log off or suspend a session?

The following table describes the different options that you or any user can use to log off, suspend, or end a session. Click Start, click Settings, click the user name (top-right corner), and then click Sign out. The session ends and the station is available for log on by any user.

Why am I getting a double login request after restarting Windows 10?

This turns out to be a new Microsoft bug which occurs on Windows 10 Creators Update specifically after restarting or shutting down a machine. Please keep in mind that you or your clients may experience this issue following the update. Here’s a quick fix that will help you avoid the double login request described above:


1 Answers

In the Account/Logout page, which lives under Areas/Identity/Account/Logout.cshtml.cs in your scaffolded ASP.NET Core Identity code, there is an OnGet handler that looks like this:

public void OnGet() { }

Because this is using ASP.NET Core Razor Pages, all this does is render the corresponding Logout.cshtml page. In your example, when you hit Logout in the MVC app, it clears its own cookies and then passes you over to the IS4 app (the OnGet, specifically). Because this OnGet handler is empty, it's not really doing anything and it's certainly not signing you out of the IS4 app.

If you look at the OnPost handler inside of Logout.cshtml.cs, you'll see it looks something like this:

public async Task<IActionResult> OnPost(string returnUrl = null)
{
    await _signInManager.SignOutAsync();
    // ...
}

This call to SignOutAsync does exactly what it suggests: it signs you out of IS4 itself. However, in your current workflow, this OnPost handler is not being called. The OnGet handler is called indirectly when you use Logout in the MVC app, as I've already mentioned.

Now, if you look at the controller/action implementation of IS4 logout in the Quickstart.UI project, you'll see that essentially it passes the GET request over to the POST request. Here's the code, with comments stripped out:

[HttpGet]
public async Task<IActionResult> Logout(string logoutId)
{
    var vm = await BuildLogoutViewModelAsync(logoutId);

    if (vm.ShowLogoutPrompt == false)
        return await Logout(vm);

    return View(vm);
}

When logging out, there's a setting that controls whether or not the user should first be prompted to confirm whether or not they want to log out. That's mostly what this code is taking care of - it passes it straight over to the POST request handler if the prompt is not required. Here's a snippet of the code for the POST:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
    var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

    if (User?.Identity.IsAuthenticated == true)
    {
        await HttpContext.SignOutAsync();

        // ...
    }

    // ...

    return View("LoggedOut", vm);
}

The important line here is the call to HttpContext.SignOutAsync - this ends up removing the cookie that IS4 is using to keep you signed in. Once this has been removed, you're signed out of IS4. Ultimately, this is what is missing from your current implementation.

At the simplest level, you can fix your issue by updating your OnGet to look like this:

public async Task<IActionResult> OnGet()
{
    if (User?.Identity.IsAuthenticated == true)
    {
        await _signInManager.SignOutAsync();          
        return RedirectToPage(); // A redirect ensures that the cookies has gone.
    }

    return Page();
}

This doesn't support the ShowLogoutPrompt option I've detailed above, simply just to keep this answer a little bit shorter. Apart from that, it's just using _signInManager to do the logout given that you're in the ASP.NET Core Identity world.

I encourage you to explore the full source-code from the Quickstart.UI implementation in order to support ShowLogoutPrompt, returnUrl, etc - I can't possibly do that here without writing a book.

like image 188
Kirk Larkin Avatar answered Sep 19 '22 21:09

Kirk Larkin