I am developing a MVC application that uses OpenID and IdentityServer3.
Background:
I am running into a issue that when the Authentication Cookie times out, I need to use the refresh token to generate a new one.
I am able to login and receive the AuthorizationCodeReceived notification, which i use to request an authorization code and retrieve a RefreshToken which I store in the claims of the AuthenticationTicket.
I have tried adding logic to check and refresh the authentication in:
- CookieAuthenticationProvider.OnValidateIdentity -- This works to refresh, and I was able to update the cookie, but it is not called after the cookie expired.
- Adding code in the begining of the the ResourceAuthorizationManager.CheckAccessAsync -- this does not work because the identity is null and I cannot retrieve the refresh token claim.
Adding a filter Filter for MVC, but I am unable to figure out what to add as a HttpResponseMessage for WebAPI.
public const string RefreshTokenKey = "refresh_token"; public const string ExpiresAtKey = "expires_at"; private const string AccessTokenKey = "access_token"; private static bool CheckAndRefreshTokenIfRequired(ClaimsIdentity id, out ClaimsIdentity identity) { if (id == null) { identity = null; return false; } if (id.Claims.All(x => x.Type != ExpiresAtKey) || id.Claims.All(x => x.Type != RefreshTokenKey)) { identity = id; return false; } //Check if the access token has expired var expiresAt = DateTime.Parse(id.FindFirstValue(ExpiresAtKey)); if ((expiresAt - DateTime.Now.ToLocalTime()).TotalSeconds < 0) { var client = GetClient(); var refreshToken = id.FindFirstValue(RefreshTokenKey); var tokenResponse = client.RequestRefreshTokenAsync(refreshToken).Result; if (tokenResponse.IsError) { throw new Exception(tokenResponse.Error); } var result = from c in id.Claims where c.Type != AccessTokenKey && c.Type != RefreshTokenKey && c.Type != ExpiresAtKey select c; var claims = result.ToList(); claims.Add(new Claim(AccessTokenKey, tokenResponse.AccessToken)); claims.Add(new Claim(ExpiresAtKey, DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString())); claims.Add(new Claim(RefreshTokenKey, tokenResponse.RefreshToken)); identity = new ClaimsIdentity(claims, id.AuthenticationType); return true; } identity = id; return false; }
Links:
How would I use RefreshTokenHandler?
Identity Server3 documentation Looked at the two examples, but using resourceowner flow for openid doesn't seem the right way. The MVC code flow relies on the User still having the principle, but my claims are all empty in the resource authorize.
EDIT: Okay, so if I set the AuthenticationTicket.Properties.ExpiresUtc to null in AuthorizationCodeReceived, it is setting it to null then somewhere down the line it is setting it to 30 days instead of 5 minutes (I searched the katana and identity server source code but could not find where it is setting this value), which I can live with, but would prefer it to be the same as the browser where it is "Session"
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
CookieManager = new SystemWebChunkingCookieManager(),
Provider = new CookieAuthenticationProvider()
{
OnValidateIdentity = context =>
{
ClaimsIdentity i;
if (CheckAndRefreshTokenIfRequired(context.Identity, out i))
{
context.ReplaceIdentity(i);
}
return Task.FromResult(0);
}
}
});
context.ReplaceIdentity()
enough to replace the cookie? – grudolf