mmr
mmr

Reputation: 383

Identity Server 4 - Log User Out when Idle

Hej Community,

I got stuck and I need some advice or pointer to a solution. I have a fairly simple Identity Server 4 Setup:

  1. Identity Server 4 with ASP.NET Identity
  2. ASP.NET Core 2.2 MVC Client

I would like to automatically log out the user after 10 minutes of inactivity. In the sample below I used 10 s to make testing somewhat quicker. The authentication, redirect and user enforced logout work as expected and like a charm using the code below. However, when the user idles for longer than the set 10 s, the user is still signed in and is not redirected to the login page at the IDS host.

The MVC client is setup using Hybrid Grant as:

Client Definition

var mvcClient = new Client
{
    ClientId = "account-mvc",
    ClientName = "Account MVC",
    ClientUri = "https://localhost:5002",

    AllowedGrantTypes = GrantTypes.Hybrid,
    ClientSecrets = { new Secret("secret".Sha256()) },

    EnableLocalLogin = true,
    RequireConsent = false,
    AllowOfflineAccess = false,
    AccessTokenLifetime = 10,   // 10 s by intention
    IdentityTokenLifetime = 10, // 10 s by intention

    RedirectUris = "https://localhost:5002/signin-oidc",
    PostLogoutRedirectUris = "https://localhost:5002/signout-callback-oidc",
    FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email,
    },
};

Identity Server Options

services.AddIdentityServer(options =>
{
    options.Authentication.CheckSessionCookieName = "auth-cookie";
    options.Authentication.CookieLifetime = new System.TimeSpan(0, 0, 10);
    options.Authentication.CookieSlidingExpiration = false;

    options.Csp.Level = IdentityServer4.Models.CspLevel.Two;

    options.Events.RaiseErrorEvents = true;
    options.Events.RaiseInformationEvents = true;
    options.Events.RaiseFailureEvents = true;
    options.Events.RaiseSuccessEvents = true;
})
.Add... // Left out for brevity

In the Startup of the MVC client I add:

MVC Client Startup

services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies", options => 
        {
            options.ExpireTimeSpan = TimeSpan.FromSeconds(10);
            options.SlidingExpiration = false;
            options.Cookie.Name = "mvc-cookie";
        })
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";

            options.Authority = "https://localhost:5001/";
            options.ClientId = "account-mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";

            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
        });

and added app.UseAuthentication() in the Configure method.

Question

How can I make sure that the user is signed out at the Identity Server once the session timed out? Any hint and help appreciated!

Upvotes: 5

Views: 9773

Answers (2)

mmr
mmr

Reputation: 383

After more debugging I found that the cookie lifetime of the MVC client worked as intended with a sliding expiration. However, once the cookie expired, the Idenity Server (IDS) was contacted and the cookie was refreshed as the session was still alive / active at the IDS.

I figured two solutions from which I decided to use Solution 1 for now and see if it is the most suitable in the long-run.

If anyone has a comment or recommendation towards security and best practice please comment or post another solution.

Solution 1: Maximum SSO lifetime of client

The Client property UserSsoLifetime (available in Identity Server 4 from v2.3) can be used to set the maximum time until a user must re-authenticate to use the client. So for the sample of the question the only required addition is in the Client Definition by adding UserSsoLifetime = 10, such as,

Client Definition

var mvcClient = new Client
{
    ClientId = "account-mvc",
    ClientName = "Account MVC",
    ClientUri = "https://localhost:5002",

    AllowedGrantTypes = GrantTypes.Hybrid,
    ClientSecrets = { new Secret("secret".Sha256()) },

    EnableLocalLogin = true,
    RequireConsent = false,
    AllowOfflineAccess = false,
    UserSsoLifetime = 10,       // <- HERE
    AccessTokenLifetime = 10,   // 10 s by intention
    IdentityTokenLifetime = 10, // 10 s by intention

    RedirectUris = "https://localhost:5002/signin-oidc",
    PostLogoutRedirectUris = "https://localhost:5002/signout-callback-oidc",
    FrontChannelLogoutUri = "https://localhost:5002/signout-oidc",

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email,
    },
};

This will force the user to re-authenticate after 10 s of inactivity.

Solution 2: OIDC Property

This SO question solves the issue with an OIDC property which can be used to force the user to re-authenticate via the login prompt once the session expired - see @Scotty Brady's answer.

So for the example denoted in the question should look like the following. Note that only the MVC client needs changes, namely the Cookie lifetime was removed and OIDC options were added forcing the re-authentication and to use the token lifetime from IDS (each line marked with a // <- HERE). This way, the cookie settings from IDS are used (sliding lifetime of 10s).

MVC Client Startup

services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies") // <- HERE
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";

            options.Authority = "https://localhost:5001/";
            options.ClientId = "account-mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";

            options.UseTokenLifetime = true; // <- HERE
            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Events.OnRedirectToIdentityProvider = context => // <- HERE
            {                                                        // <- HERE
                context.ProtocolMessage.Prompt = "login";            // <- HERE
                return Task.CompletedTask;                           // <- HERE
            };                                                       // <- HERE

            options.Scope.Add("openid");
            options.Scope.Add("profile");
            options.Scope.Add("email");
        });

Upvotes: 8

Kavindu Dodanduwa
Kavindu Dodanduwa

Reputation: 13059

You can utilize End Session Endpoint of the IdentityServer.

The end session endpoint can be used to trigger single sign-out (see spec).

To use the end session endpoint a client application will redirect the user’s browser to the end session URL. All applications that the user has logged into via the browser during the user’s session can participate in the sign-out.

You can utilize this endpoint to redirect end user back to login page using post_logout_redirect_uri paramter.

Upvotes: 1

Related Questions