0

I got stuck and need some advice or pointer to a solution. I have a fairly simple IdentityServer4 setup for our single sign on Implementation.

Three Apps

IdentityServer4 with asp.net core identity (ID server)

ASP.NET Core 2.2 MVC (client1)

ASP.NET Core 2.2 MVC (client2) The MVC client is setup using Hybrid Grant

Two scenarios:

  1. If user is active in any one of the clients(example is client1) then user should not be logged out in client2 after reaching idle timeout in that app(client2).
  2. If user is inactive in both the clients(client1 and client2) then System should log out user from all the clients(client1 and client2) when idle timeout exceeds.

Scenario 1: User active in any one of the clients. User active in client1 and idle in client2 Expected behavior: System should not log out from client2 when idle timeout exceeds. Current Identity Behavior: Able to continue works in client1, but client2 and ID server navigates to login page after idle time exceed.

Scenario 2: User is inactive in all 2 clients (client1 and client2) Expected behavior: System should log out user from the all 2 clients and ID server when idle timeout exceeds.

If I set cookie expire time in ID server only (removed slidingexpiration is true and cookie expire time in both client1 and client2) then the client apps are working continuously without expire time even though both the clients are idle until ID server expire time exceeds, client apps are working continuously.

I would like to know if expected behavior can be achieved

Clients:

 .AddCookie(options =>
                {
                    // Configure the client application to use sliding sessions
                    options.SlidingExpiration = true;
                    // Expire the session of 15 minutes of inactivity
                    options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
                })

ID server:

services.AddIdentityServer(options =>
            {
                options.Authentication.CookieLifetime = TimeSpan.FromMinutes(15);
                options.Authentication.CookieSlidingExpiration = true;
            })
roczstar
  • 43
  • 2
  • 6

2 Answers2

1

The question that needs to be answered first is: what system, where?

The browser clients can't be trusted and should not interfere. I think it's best to restore the cookie settings. Don't let the front try to trigger a logout.

It's best to keep track of the users in the backend. You can accomplish this by adding a service in the Mvc client that 1. registers the user as active by IdentityServer, 2. keeps track of activity and 3. unregisters the user by IdentityServer when the user seems inactive too long. Take a look at the token cleanup service that comes with IdentityServer for some inspiration on how you can update the list using an interval.

In startup of both the Mvc clients add a handler for the cookies:

Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";
    })
    .AddCookie("Cookies", options =>
    {
        // you'll need to create your own handler
        options.EventsType = typeof(MyCookieEventHandler);
    })

I won't add code here but rather explain the purpose: the cookiehandler is called every time a user needs to access a secured source. This means that you can update the users activity timestamp (that you use to keep track of the user) here, meaning that you don't have to contact IdentityServer all the time. The service should contact IdentityServer only when there is a status change: user has become active or inactive.

Now you need to add a service to IdentityServer in order to keep track of the users in a central place. You can persist the status of the user in a store. Add a user (per client) when it becomes active and remove the user when it is no longer active. When the last session of a user is removed from this list, trigger a backchannel logout.

A backchannel logout can signal the mvc clients that a user is logged out. Using the CookieEventHandler the user is then denied access. This doesn't update the front on logout, but is effective when the user contacts the mvc client. It will find itself logged out of all apps and Identityserver.

When you follow the link you'll see an implementation of the CookieEventHandler. And search the internet for backchannel logout, you may find examples that you can use.

  • few questions 1. Do I have to remove Options.Authentication.CookieLifetime = TimeSpan.FromMinutes(15); options.Authentication.CookieSlidingExpiration = true; in IdentityServer ? 2. The service should contact IdentityServer only when there is a status change: user has become active or inactive. - how service in MVC can contact IdentityServer ? 3. When the last session of a user is removed from this list- Where and when exactly I need to check this list ? – roczstar Jul 15 '19 at 15:55
  • 1. I don't know the defaults, but you can use long-lived cookies since the CookieEventHandler is now responsible for cookie lifetime. 2. Extend Ids4 with a new endpoint where you can update the status, e.g. with httpclient. 3. Create a new table (for ids4) where you store the user/client - timestamp. The client updates the status, while the cleanup service removes al entries where the timestamp is exceeded. As soon as the last record is removed from the store (by either ids4/client) perform a full logout by notifying all active clients (available through the Usersession). Does this help? –  Jul 15 '19 at 19:31
  • Well, that makes sense. But the problem here is if user login in different browsers( chrome/firefox). We have to store user/client/browser. Is there a way to identify the browser ? How can I add cookiehandler in angular application? – roczstar Jul 17 '19 at 15:58
  • You can identify the session by the sessionId (sid). Problem is that the id is not shared across browsers. The backchannel logout samples will only logout a user from all apps that are in the same browser. If you want this cross-browser, then you can't use cookies but you'll have to keep track of the user in another way. I'm really not into javascript frameworks, but perhaps [this article and website](https://damienbod.com/2017/06/11/openid-connect-session-management-an-angular-application-using-identityserver4/) can help you with that. –  Jul 17 '19 at 16:24
  • I tried sessionId, it is different for all apps. In my case I need to logout a user from all apps that are opened in that particular browser after reaching timeout. For example user is active in chrome and inactive in firefox then I need to logout user from all apps in only firefox. Is it achievable? – roczstar Jul 17 '19 at 16:49
  • Great answer but the link is dead – cokceken Nov 25 '20 at 14:26
1

The approach we've taken, and the approach that I think is in the spirit of the OIDC spec is that the IDP session is the master and can be long lived. The IDP doesn't care about client sessions but the clients should monitor the IDP session (session monitoring spec) and when the IDP is signed out the client sessions should also be signed out (either front or back channel). Explicit sign out from a client should also sign you out of your IDP session.

Also, since the protocol supports prompt=login and max_age=n so you can enforce interactive authentication even if an active session already exists on the IDP. This allows clients to implement their own policies for how often users have to authenticate - e.g. to access admin function you have to have authenticated within the last 5 minutes or something like that.

What would also be neat is if the session monitoring kicked in when the IDP auth cookie expires but out of the box this does not happen with IDS4. However it should be possible to create a custom IUserSession implementation that aligns the session ID cookie with the main auth cookie properly.

mackie
  • 4,996
  • 1
  • 17
  • 17