Search code examples
asp.net-coreangular5identityserver4logout

Logout of MVC Application From Angular 5


We have an angular 5 application built on top of MVC authentication. The application is served up from the Home/Index action, and once the application is loaded, the angular routing takes care of pretty much everything. We are doing this primarily because we wanted to use MVC's Authentication processes (we are using Identity Server 4 as our Oath system).

This works well with one exception: logout. When we attempt to logout, the application seems to be immediately reauthorized and reloads instead of returning us to our Identity Server login page.

Originally, we had success in our development environment through this code:

[HttpPost]
public async Task<IActionResult> Logout()
{
  foreach (string key in Request.Cookies.Keys)
  {
    Response.Cookies.Delete(key);
  }

  await HttpContext.SignOutAsync();

  return Ok();
}

But it was a false positive, because all of our applications were running on localhost, so they had access to each other's cookies. Due to this, the Identity Server cookies were cleared along with the cookies for the Angular application.

We attempted to replace that logic with something like this:

    public async Task Logout()
    {
        if (User?.Identity.IsAuthenticated == true)
        {
            // delete local authentication cookie
            await HttpContext.SignOutAsync("Cookies");
            await HttpContext.SignOutAsync("oidc");
        }
    }

However, it did not log out in either environment (however that code works for one of our MVC applications using the same identity server).

To give some background, our logout process for the Angular application comes from a material menu, where we then prompt the user if they really want to logout with a modal, before calling the logout function. Code snippets of this process:

Method called by logout button:

public openDialog(): void {
    let dialogRef = this.dialog.open(ModalComponent, {
      width: '250px',
      data: { text: this.logoutText, ok: this.okText, cancel: this.cancelText }
    });

    dialogRef.afterClosed().subscribe(result => {
      if (result !== undefined) {
        switch (result.data) {
          case 'ok':
            this.logout();
            break;
          case 'cancel':
            break;
          default:
            break;
        }
      }
   });
}

private logout(): void {
   this.sessionService.logOut();
}

Session service:

public logOut(): void {
    this.auth.revokeToken();
    this.http.post(this.logoutUrl, undefined).subscribe(x => window.location.reload());
}

As mentioned, calling logout this way ends up with a page refresh and the user not really being logged out. There's probably something simple we are missing, but all of the tinkering I've done so far has not led to any success.

EDIT:

Our angular routing is fairly simple (though the pathing prevents us from calling something like home/logout):

{
  path: 'parts',
  component: PartsComponent,
  canActivate: [AuthGuard],
  runGuardsAndResolvers: 'always',
  data: { title: 'Parts' }
},
{
  path: '',
  redirectTo: 'parts',
  pathMatch: 'full'
},
{
  path: '**',
  redirectTo: 'parts'
}

Our MVC routes are also fairly straightforward

    app.UseMvc(routes =>
    {
      routes.MapRoute(
       name: "default",
       template: "{controller=Home}/{action=Index}/{id?}");

      routes.MapRoute(
       "Sitemap",
       "sitemap.xml",
       new { controller = "Home", action = "SitemapXml" });

      routes.MapSpaFallbackRoute(
        name: "spa-fallback",
        defaults: new { controller = "Home", action = "Index" });
    });

I'm not certain how we could easily route to home/logout with the angular routes the way they are. I'm guessing we'd have to add a route for it. We tried that at one point but it never routed correctly. I'm trying to find my notes on what we tried.


Solution

  • After you cleared the local cookies of your application send the user to the end_session_endpoint of your Idsrv. (The code you showed to clear your session should work, if not I'd the config in startup.cs and debug to check if the redirect really removed the cookie in the browser).

    E.g. https://demo.identityserver.io/.well-known/openid-configuration

    There you see the endpoint. This should remove the session on the Idsrv. Depending on your setup this could kill your sessions on your other applications that uses the same Identity Server instance.

    You can read more about this in the docs.

    I don't see a problem with the Angular routing interfering with your server side routing. As long as you ofcourse really do a redirect to that page using a regular window.location.replace

    And ofcourse like @Win mentioned it would be a good security guideline to revoke refresh_token and reference_tokens if you use those.