Also refresh expires/Issues when renewing cookie

This commit is contained in:
Hao Kung 2016-01-08 13:22:55 -08:00
parent 7a23028527
commit 5bcc1bfc26
2 changed files with 94 additions and 25 deletions

View File

@ -21,9 +21,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
private const string HeaderValueMinusOne = "-1";
private const string SessionIdClaim = "Microsoft.AspNet.Authentication.Cookies-SessionId";
private bool _shouldRenew;
private DateTimeOffset? _renewIssuedUtc;
private DateTimeOffset? _renewExpiresUtc;
private bool _shouldRefresh;
private DateTimeOffset? _refreshIssuedUtc;
private DateTimeOffset? _refreshExpiresUtc;
private string _sessionKey;
private Task<AuthenticateResult> _readCookieTask;
@ -37,6 +37,33 @@ namespace Microsoft.AspNet.Authentication.Cookies
return _readCookieTask;
}
private void CheckForRefresh(AuthenticationTicket ticket)
{
var currentUtc = Options.SystemClock.UtcNow;
var issuedUtc = ticket.Properties.IssuedUtc;
var expiresUtc = ticket.Properties.ExpiresUtc;
var allowRefresh = ticket.Properties.AllowRefresh ?? true;
if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration && allowRefresh)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
var timeRemaining = expiresUtc.Value.Subtract(currentUtc);
if (timeRemaining < timeElapsed)
{
RequestRefresh(ticket);
}
}
}
private void RequestRefresh(AuthenticationTicket ticket)
{
_shouldRefresh = true;
var currentUtc = Options.SystemClock.UtcNow;
_refreshIssuedUtc = currentUtc;
var timeSpan = ticket.Properties.ExpiresUtc.Value.Subtract(ticket.Properties.IssuedUtc.Value);
_refreshExpiresUtc = currentUtc.Add(timeSpan);
}
private async Task<AuthenticateResult> ReadCookieTicket()
{
var cookie = Options.CookieManager.GetRequestCookie(Context, Options.CookieName);
@ -79,20 +106,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
return AuthenticateResult.Fail("Ticket expired");
}
var allowRefresh = ticket.Properties.AllowRefresh ?? true;
if (issuedUtc != null && expiresUtc != null && Options.SlidingExpiration && allowRefresh)
{
var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
var timeRemaining = expiresUtc.Value.Subtract(currentUtc);
if (timeRemaining < timeElapsed)
{
_shouldRenew = true;
_renewIssuedUtc = currentUtc;
var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value);
_renewExpiresUtc = currentUtc.Add(timeSpan);
}
}
CheckForRefresh(ticket);
// Finally we have a valid ticket
return AuthenticateResult.Success(ticket);
@ -116,7 +130,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
if (context.ShouldRenew)
{
_shouldRenew = true;
RequestRefresh(result.Ticket);
}
return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme));
@ -144,7 +158,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
protected override async Task FinishResponseAsync()
{
// Only renew if requested, and neither sign in or sign out was called
if (!_shouldRenew || SignInAccepted || SignOutAccepted)
if (!_shouldRefresh || SignInAccepted || SignOutAccepted)
{
return;
}
@ -153,13 +167,13 @@ namespace Microsoft.AspNet.Authentication.Cookies
var ticket = (await HandleAuthenticateOnceAsync())?.Ticket;
if (ticket != null)
{
if (_renewIssuedUtc.HasValue)
if (_refreshIssuedUtc.HasValue)
{
ticket.Properties.IssuedUtc = _renewIssuedUtc;
ticket.Properties.IssuedUtc = _refreshIssuedUtc;
}
if (_renewExpiresUtc.HasValue)
if (_refreshExpiresUtc.HasValue)
{
ticket.Properties.ExpiresUtc = _renewExpiresUtc;
ticket.Properties.ExpiresUtc = _refreshExpiresUtc;
}
if (Options.SessionStore != null && _sessionKey != null)
@ -175,9 +189,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
var cookieValue = Options.TicketDataFormat.Protect(ticket, GetTlsTokenBinding());
var cookieOptions = BuildCookieOptions();
if (ticket.Properties.IsPersistent && _renewExpiresUtc.HasValue)
if (ticket.Properties.IsPersistent && _refreshExpiresUtc.HasValue)
{
cookieOptions.Expires = _renewExpiresUtc.Value.ToUniversalTime().DateTime;
cookieOptions.Expires = _refreshExpiresUtc.Value.ToUniversalTime().DateTime;
}
Options.CookieManager.AppendResponseCookie(

View File

@ -580,6 +580,61 @@ namespace Microsoft.AspNet.Authentication.Cookies
Assert.Null(FindClaimValue(transaction5, ClaimTypes.Name));
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public async Task ShouldRenewUpdatesIssuedExpiredUtc(bool sliding)
{
var clock = new TestClock();
DateTimeOffset? lastValidateIssuedDate = null;
DateTimeOffset? lastExpiresDate = null;
var server = CreateServer(options =>
{
options.SystemClock = clock;
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = sliding;
options.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = ctx =>
{
lastValidateIssuedDate = ctx.Properties.IssuedUtc;
lastExpiresDate = ctx.Properties.ExpiresUtc;
ctx.ShouldRenew = true;
return Task.FromResult(0);
}
};
},
context =>
context.Authentication.SignInAsync("Cookies",
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies")))));
var transaction1 = await SendAsync(server, "http://example.com/testpath");
var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue);
Assert.NotNull(transaction2.SetCookie);
Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name));
Assert.NotNull(lastValidateIssuedDate);
Assert.NotNull(lastExpiresDate);
var firstIssueDate = lastValidateIssuedDate;
var firstExpiresDate = lastExpiresDate;
clock.Add(TimeSpan.FromMinutes(1));
var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction2.CookieNameValue);
Assert.NotNull(transaction3.SetCookie);
Assert.Equal("Alice", FindClaimValue(transaction3, ClaimTypes.Name));
clock.Add(TimeSpan.FromMinutes(2));
var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction3.CookieNameValue);
Assert.NotNull(transaction4.SetCookie);
Assert.Equal("Alice", FindClaimValue(transaction4, ClaimTypes.Name));
Assert.NotEqual(lastValidateIssuedDate, firstIssueDate);
Assert.NotEqual(firstExpiresDate, lastExpiresDate);
}
[Fact]
public async Task CookieExpirationCanBeOverridenInEvent()