Search code examples
identityserver4owin-middleware

How can I refresh an expired access token when it’s stored in a claim in a cookie?


Using Identity Server 4 server.

In client’s Startup.Auth.cs :

private static void ConfigureAuth(IAppBuilder app)
    {
        ISettingsReader settingsReader = Services.Resolve<ISettingsReader>();

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            ExpireTimeSpan = new TimeSpan(1, 0, 0),
            CookieSecure = CookieSecureOption.Always,
            CookieHttpOnly = true,
            SlidingExpiration = true
        });

        var platformUri = settingsReader.GetSetting("PlatformUri")?.TrimEnd('/');
        var platformApiKey = settingsReader.GetSetting("PlatformApiKey");
        var deploymentURL = settingsReader.GetSetting("deploymentURL")?.TrimEnd('/');

        var authority = $"{platformUri}/identity";

        string clientSecret;
        string clientId = SplitApiKey(platformApiKey, out clientSecret);

        var options = new OpenIdConnectAuthenticationOptions
        {
            ClientId = clientId,
            ClientSecret = clientSecret,
            Authority = authority,
            RedirectUri = $"{deploymentURL}/signin/callback",
            ResponseType = "id_token token",
            Scope = "platform openid",
            UseTokenLifetime = false,
            SignInAsAuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Notifications = new OpenIdConnectAuthenticationNotifications
            {
                SecurityTokenValidated = SecurityTokenValidatedHandler,
                RedirectToIdentityProvider = RedirectToIdentityProviderHandler,
            }
        };

        app.UseOpenIdConnectAuthentication(options);

        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
    }

Note – the cookie’s expire timespan has been set to 5 mins for debugging, normally would be set to an hour.

And then stash the access_token in the validated handler (as per several articles) so that we can use it later for api calls:

        private static async Task SecurityTokenValidatedHandler(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
    {
        var jwtDetails = JsonWebToken.Parse(notification.ProtocolMessage.AccessToken);

        notification.AuthenticationTicket.Identity.AddClaims(
            jwtDetails.Claims.Where(c => DesiredAccessTokenClaims.Contains(c.Type)));

        notification.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", notification.ProtocolMessage.AccessToken));
        notification.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", notification.ProtocolMessage.IdToken));
    }

This works fine. However, although the cookies do auto refresh themselves, with sliding expiration (typically after 2-3 minutes, rather than the full 5), there doesn’t seem to be a way to refresh the access token which is held in the claim, so although the user will remain logged in, the access token will be useless after it expires.

Is this the right way to be going about this? And if so, is there a way to update the access token in the claim, in the background without disturbing the user? It seems that the ideal solution would be to have the cookie refresh also trigger the SecurityTokenValidatedHandler so that a renewed claim can be added into the new cookie, although despite looking in CookieManager, etc. there doesn't seem to be an event which is triggered when the cookie sliding refreshes itself. Does anyone know a way to do this?

Many thanks for your time!


Solution

  • If anyone comes across this issue, the answer was to change from implicit to hybrid. Hybrid allows refresh tokens, whereas implicit, not so much. In our case we needed Hybrid and Client Creds