Hook into IdentityServer4 session cookie sliding expiration

998 Views Asked by At

I need to run some custom code (manage another custom cookie), at the moment when IdentityServer performs the sliding of the expiration time on the session cookie (idsrv).

How or where can I hook into the IdentityServer pipeline to accomplish this? Is there something I can override or is there an event I can subscribe to?

I am currently using IdentityServer4 3.x in an asp.net core 3.1 app.

2

There are 2 best solutions below

0
nahidf On

AddIdentityServer extension is just adding default cookie handlers. And setting the CookieAuthenticationOptions props based on value passed.

Based on official description, sliding expiration works like this:

The SlidingExpiration is set to true to instruct the handler to re-issue a new cookie with a new expiration time any time it processes a request which is more than halfway through the expiration window.

considering these, if you need do sth on the sliding of the expiration time on the session cookie you need to have a custom cookie handler which does things differently

0
jedigo On

This is using the standard cookie handler.

This does not emit any external events whenever a cookie is refreshed, however there are some ways to detect this via side effects.

If you are using a custom ITicketStore, then you can use StoreAsync to detect when the ticket is being changed.

If you are not using a custom ticket store, you can attach a middleware before the authentication middleware is attached:

public class CookieSlidingMiddleware
{
    private readonly RequestDelegate next;
    
    public CookieSlidingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        httpContext.Response.OnStarting(ResponseStarting, httpContext);
        await next(httpContext);
    }

    public async Task ResponseStarting(object contextObject)
    {
        HttpContext httpContext = (HttpContext)contextObject;
        var reponseCookies = httpContext.Response.Cookies;

        if (httpContext.Response.Headers.ContainsKey("Set-Cookie"))
        {
            foreach (var setCookie in httpContext.Response.Headers["Set-Cookie"])
            {
                var cookie = SetCookieHeaderValue.Parse(setCookie);
                if (cookie.Name == "idsrv")
                {
                    if (cookie.Expires == null)
                    {
                        // The cookie is a session cookie, and not sliding
                        return;
                    }
                    else if (cookie.Expires == DateTime.UnixEpoch)
                    {
                        // the cookie is being deleted
                        return;
                    }
                    else if (!httpContext.Request.Cookies.ContainsKey("idsrv"))
                    {
                        // The cookie is being set for the first time
                        return;
                    }
                    else
                    {
                        // the cookie is being refreshed
                        
                        // Do stuff here;
                        return;
                    }
                }
            }
        }
    }
}

The cookie authentication handles the cookie refresh in it's own event handler for Response.OnStarting.

This event handler is currently implemented as stack of functions, and so is called in reverse order (i.e. this middleware must be called before the authentication middleware).

However, I don't think there are any guarantees about this calling order, so in future it may be necessary to switch the order.