Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Open.IdentityServer/src/Extensions/TokenExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ public static class TokenExtensions
/// <exception cref="Exception">Thrown when a JSON-valued claim cannot be parsed, two conflicting JSON claims exist for the same key, or an unsupported JSON value type is encountered.</exception>
public static JwtPayload CreateJwtPayload(this Token token, TimeProvider clock, IdentityServerOptions options, ILogger logger)
{
var now = clock.GetUtcNow();
var payload = new JwtPayload(
token.Issuer,
null,
null,
clock.GetUtcNow().UtcDateTime,
clock.GetUtcNow().UtcDateTime.AddSeconds(token.Lifetime));
now.UtcDateTime,
now.UtcDateTime.AddSeconds(token.Lifetime));

foreach (var aud in token.Audiences)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,9 @@ public virtual async Task<Token> CreateIdentityTokenAsync(TokenCreationRequest r
claims.Add(new Claim(JwtClaimTypes.Nonce, request.Nonce));
}

var now = Clock.GetUtcNow();
// add iat claim
claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.GetUtcNow().ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64));
claims.Add(new Claim(JwtClaimTypes.IssuedAt, now.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64));

// add at_hash claim
if (request.AccessTokenToHash.IsPresent())
Expand Down Expand Up @@ -172,7 +173,7 @@ public virtual async Task<Token> CreateIdentityTokenAsync(TokenCreationRequest r

var token = new Token(OidcConstants.TokenTypes.IdentityToken)
{
CreationTime = Clock.GetUtcNow().UtcDateTime,
CreationTime = now.UtcDateTime,
Audiences = { request.ValidatedRequest.Client.ClientId },
Issuer = issuer,
Lifetime = request.ValidatedRequest.Client.IdentityTokenLifetime,
Expand Down Expand Up @@ -218,14 +219,15 @@ public virtual async Task<Token> CreateAccessTokenAsync(TokenCreationRequest req
claims.Add(new Claim(JwtClaimTypes.SessionId, request.ValidatedRequest.SessionId));
}

var now = Clock.GetUtcNow();
// iat claim as required by JWT profile
claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.GetUtcNow().ToUnixTimeSeconds().ToString(),
claims.Add(new Claim(JwtClaimTypes.IssuedAt, now.ToUnixTimeSeconds().ToString(),
ClaimValueTypes.Integer64));

var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri();
var token = new Token(OidcConstants.TokenTypes.AccessToken)
{
CreationTime = Clock.GetUtcNow().UtcDateTime,
CreationTime = now.UtcDateTime,
Issuer = issuer,
Lifetime = request.ValidatedRequest.AccessTokenLifetime,
Claims = claims.Distinct(new ClaimComparer()).ToList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,30 +61,31 @@ public async Task<bool> ShouldSlowDown(string deviceCode, DeviceCode details)
using var trace = _telemetry.Trace(TelemetryConstants.TraceCategories.Services, this);

var key = KeyPrefix + deviceCode;
var options = new DistributedCacheEntryOptions {AbsoluteExpiration = _clock.GetUtcNow().AddSeconds(details.Lifetime)};
var now = _clock.GetUtcNow();
var options = new DistributedCacheEntryOptions { AbsoluteExpiration = now.AddSeconds(details.Lifetime) };

var lastSeenAsString = await _cache.GetStringAsync(key);

// record new
if (lastSeenAsString == null)
{
await _cache.SetStringAsync(key, _clock.GetUtcNow().ToString("O"), options);
await _cache.SetStringAsync(key, now.ToString("O"), options);
return false;
}

// check interval
if (DateTime.TryParse(lastSeenAsString, out var lastSeen))
{
var client = await _clientStore.FindEnabledClientByIdAsync(details.ClientId);
if (_clock.GetUtcNow() < lastSeen.AddSeconds(client?.PollingInterval ?? _options.DeviceFlow.Interval))
if (now < lastSeen.AddSeconds(client?.PollingInterval ?? _options.DeviceFlow.Interval))
{
await _cache.SetStringAsync(key, _clock.GetUtcNow().ToString("O"), options);
await _cache.SetStringAsync(key, now.ToString("O"), options);
return true;
}
}

// store current and continue
await _cache.SetStringAsync(key, _clock.GetUtcNow().ToString("O"), options);
await _cache.SetStringAsync(key, now.ToString("O"), options);
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
return;
}

var issued = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime;
// If the session id or subject id has changed, remove any previous grants and create a new session
if(existingSession.SessionId != ticket.Properties.GetSessionId() ||
existingSession.SubjectId != ticket.Principal.GetSubjectId())
Expand All @@ -104,7 +105,7 @@
Types = IdentityServerConstants.PersistedGrantTypes.PersistedGrantTokenTypes,
});

existingSession.Created = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime;
existingSession.Created = issued;
}

logger.RenewingTicketInStore(key, ticket.Properties.ExpiresUtc);
Expand All @@ -113,7 +114,7 @@
existingSession.SubjectId = ticket.Principal.GetSubjectId();
existingSession.SessionId = ticket.Properties.GetSessionId();
existingSession.DisplayName = ticket.Principal.FindFirstValue(JwtClaimTypes.Name);
existingSession.Renewed = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime;
existingSession.Renewed = issued;
existingSession.Expires = ticket.Properties.ExpiresUtc?.UtcDateTime;
existingSession.Data = ToProtectedDataString(ticket);

Expand Down Expand Up @@ -172,7 +173,7 @@
}

/// <inheritdoc />
public async Task<IEnumerable<AuthenticationTicketFilterResult>> FilterServerAuthenticationTickets(string subjectId, string sessionId)

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Nullability of reference types in type of parameter 'sessionId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).

Check warning on line 176 in src/Open.IdentityServer/src/Stores/Default/ServerSessionTicketStore.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Nullability of reference types in type of parameter 'subjectId' of 'Task<IEnumerable<AuthenticationTicketFilterResult>> ServerSessionTicketStore.FilterServerAuthenticationTickets(string subjectId, string sessionId)' doesn't match implicitly implemented member 'Task<IEnumerable<AuthenticationTicketFilterResult>> IServerSessionTicketStore.FilterServerAuthenticationTickets(string? subjectId, string? sessionId)' (possibly because of nullability attributes).
{
using ITrace? trace = telemetry.Trace(TelemetryConstants.TraceCategories.Stores, this);

Expand Down Expand Up @@ -227,16 +228,17 @@
string? displayName = string.IsNullOrWhiteSpace(options.ServerSideSessions.UserDisplayNameClaimType)
? null
: ticket.Principal.FindFirstValue(options.ServerSideSessions.UserDisplayNameClaimType);


var issued = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime;
ServerSideSession serverSideSession = new ServerSideSession
{
Key = key,
Scheme = ticket.AuthenticationScheme,
SubjectId = ticket.Principal.GetSubjectId(),
SessionId = ticket.Properties.GetSessionId(),
DisplayName = displayName,
Created = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime,
Renewed = ticket.Properties.IssuedUtc?.UtcDateTime ?? timeProvider.GetUtcNow().UtcDateTime,
Created = issued,
Renewed = issued,
Expires = ticket.Properties.ExpiresUtc?.UtcDateTime,
Data = ToProtectedDataString(ticket),
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,15 @@ public async Task<SecretValidationResult> ValidateAsync(IEnumerable<Secret> secr
{
var secretsArray = secrets as Secret[] ?? secrets.ToArray();

var expiredSecrets = secretsArray.Where(s => s.Expiration.HasExpired(_clock.GetUtcNow().UtcDateTime)).ToList();
var now = _clock.GetUtcNow();
var expiredSecrets = secretsArray.Where(s => s.Expiration.HasExpired(now.UtcDateTime)).ToList();
if (expiredSecrets.Any())
{
expiredSecrets.ForEach(
ex => _logger.LogInformation("Secret [{description}] is expired", ex.Description ?? "no description"));
}

var currentSecrets = secretsArray.Where(s => !s.Expiration.HasExpired(_clock.GetUtcNow().UtcDateTime)).ToArray();
var currentSecrets = secretsArray.Where(s => !s.Expiration.HasExpired(now.UtcDateTime)).ToArray();

// see if a registered validator can validate the secret
foreach (var validator in _validators)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,16 @@ public void Should_ignore_standard_validation_claims_in_token_claims()
payload.NotBefore.Should().Be(expectedNotBefore);
payload.Expiration.Should().Be(expectedExpiry);
}

[Fact]
public void Should_take_not_before_and_expiration_from_one_clock_read()
{
IdentityServerOptions options = new IdentityServerOptions();
// every read moves the clock on, so separate reads give different seconds
FakeTimeProvider fakeTimeProvider = new() { AutoAdvanceAmount = TimeSpan.FromSeconds(1) };

JwtPayload payload = _token.CreateJwtPayload(fakeTimeProvider, options, TestLogger.Create<JwtPayloadCreationTests>());

(payload.Expiration - payload.NotBefore).Should().Be(_token.Lifetime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Modified by Rock Solid Knowledge Ltd. Copyright in modifications 2026, Rock Solid Knowledge Ltd.
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.

using System;
using System.Linq;
using System.Threading.Tasks;
using AwesomeAssertions;
Expand All @@ -12,6 +13,7 @@
using Open.IdentityServer.Validation;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Time.Testing;
using Microsoft.IdentityModel.Tokens;
using Moq;
using Open.IdentityServer;
Expand Down Expand Up @@ -41,12 +43,17 @@ public DefaultTokenServiceTests()
svcs.AddSingleton(_options);
_httpContext.RequestServices = svcs.BuildServiceProvider();

_subject = new DefaultTokenService(
_subject = CreateSubject(_mockSystemClock);
}

private DefaultTokenService CreateSubject(TimeProvider clock)
{
return new DefaultTokenService(
_mockClaimsService,
_mockReferenceTokenStore,
_mockTokenCreationService,
new HttpContextAccessor { HttpContext = _httpContext },
_mockSystemClock,
clock,
_mockKeyMaterialService,
_options,
_telemetry.Object,
Expand Down Expand Up @@ -194,6 +201,51 @@ public async Task CreateAccessTokenAsync_when_session_should_include_sid()
result.Claims.SingleOrDefault(x => x.Type == JwtClaimTypes.SessionId).Value.Should().Be("123");
}

[Fact]
public async Task CreateAccessTokenAsync_WhenClockMovesBetweenReads_ShouldMatchIatToCreationTime()
{
// every read moves the clock on, so separate reads give different seconds
var subject = CreateSubject(new FakeTimeProvider { AutoAdvanceAmount = TimeSpan.FromSeconds(1) });

var request = new TokenCreationRequest
{
ValidatedResources = new ResourceValidationResult(),
ValidatedRequest = new ValidatedRequest()
{
Client = new Client { }
}
};

var result = await subject.CreateAccessTokenAsync(request);

result.Claims.Single(x => x.Type == JwtClaimTypes.IssuedAt).Value
.Should().Be(new DateTimeOffset(result.CreationTime).ToUnixTimeSeconds().ToString());
}

[Fact]
public async Task CreateIdentityTokenAsync_WhenClockMovesBetweenReads_ShouldMatchIatToCreationTime()
{
_mockKeyMaterialService.SigningCredentials.Add(
new SigningCredentials(CryptoHelper.CreateRsaSecurityKey(), "RS256"));

// every read moves the clock on, so separate reads give different seconds
var subject = CreateSubject(new FakeTimeProvider { AutoAdvanceAmount = TimeSpan.FromSeconds(1) });

var request = new TokenCreationRequest
{
ValidatedResources = new ResourceValidationResult(),
ValidatedRequest = new ValidatedRequest()
{
Client = new Client { }
}
};

var result = await subject.CreateIdentityTokenAsync(request);

result.Claims.Single(x => x.Type == JwtClaimTypes.IssuedAt).Value
.Should().Be(new DateTimeOffset(result.CreationTime).ToUnixTimeSeconds().ToString());
}

[Fact]
public async Task CreateIdentityTokenAsync_WhenCalled_ShouldInitiateTelemetryTrace()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using AwesomeAssertions;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Time.Testing;
using Moq;
using Open.IdentityServer.Configuration;
using Open.IdentityServer.Models;
Expand Down Expand Up @@ -62,13 +63,27 @@ public async Task First_Poll()
CheckCacheEntry(handle);
}

private DistributedDeviceFlowThrottlingService CreateSubject()
[Fact]
public async Task First_Poll_Should_Expire_Cache_Entry_Lifetime_After_Stored_Time()
{
var handle = Guid.NewGuid().ToString();
// every read moves the clock on, so separate reads give different times
var service = CreateSubject(new FakeTimeProvider { AutoAdvanceAmount = TimeSpan.FromSeconds(1) });

await service.ShouldSlowDown(handle, deviceCode);

cache.Items.TryGetValue(CacheKey + handle, out var values).Should().BeTrue();
var storedTime = DateTimeOffset.Parse(Encoding.UTF8.GetString(values.Item1));
values.Item2.AbsoluteExpiration.Should().Be(storedTime.AddSeconds(deviceCode.Lifetime));
}

private DistributedDeviceFlowThrottlingService CreateSubject(TimeProvider clock = null)
{
return new DistributedDeviceFlowThrottlingService(
cache,
cache,
_clientStore.Object,
new StubClock {UtcNowFunc = () => testDate},
options,
clock ?? new StubClock {UtcNowFunc = () => testDate},
options,
_telemetry.Object);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,28 @@ public async Task StoreAsync_WhenOptionalValuesNotProvided_ShouldUseCorrectDefau
dataProtector.ValidateProtectedData(actualPayload, expectedJson);
}

[Fact]
public async Task StoreAsync_WhenIssuedUtcNotProvided_ShouldSetCreatedAndRenewedFromOneClockRead()
{
AuthenticationTicket authenticationTicket = ServerSessionTestGenerators.GenerateAuthenticationTicket(
"FakeAuthScheme", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());

ServerSideSession? createdSessionModel = null;
Mock.Get(serverServerSideSessionStore)
.Setup(x => x.CreateSession(It.IsAny<ServerSideSession>()))
.Callback<ServerSideSession>((session) => { createdSessionModel = session; });

// every read moves the clock on, so separate reads give different times
fakeTimeProvider.AutoAdvanceAmount = TimeSpan.FromSeconds(1);

ServerSessionTicketStore sut = CreateSut();

await sut.StoreAsync(authenticationTicket);

createdSessionModel.Should().NotBeNull();
createdSessionModel.Renewed.Should().Be(createdSessionModel.Created);
}

[Fact]
public async Task StoreAsync_WhenOptionalValuesProvided_ShouldUseThem()
{
Expand Down Expand Up @@ -335,6 +357,36 @@ public async Task RenewAsync_WhenSessionOverwritten_ShouldExpirePreviousGrants()
)));
}

[Fact]
public async Task RenewAsync_WhenSessionOverwrittenWithoutIssuedUtc_ShouldSetCreatedAndRenewedFromOneClockRead()
{
ServerSideSession existingSession = new ServerSideSession
{
Key = Guid.NewGuid().ToString(), Scheme = "AuthScheme",
SessionId = "session_id",
SubjectId = "subject_id",
Created = new DateTime(2026, 1, 1, 12, 0, 0, DateTimeKind.Utc),
Renewed = new DateTime(2026, 1, 2, 12, 0, 0, DateTimeKind.Utc),
Data = "EXISTING_PROTECTED_DATA",
};

AuthenticationTicket authenticationTicket = ServerSessionTestGenerators.GenerateAuthenticationTicket(
"FakeAuthScheme", Guid.NewGuid().ToString(), Guid.NewGuid().ToString());

Mock.Get(serverServerSideSessionStore)
.Setup(x => x.GetSession(existingSession.Key))
.ReturnsAsync(existingSession);

// every read moves the clock on, so separate reads give different times
fakeTimeProvider.AutoAdvanceAmount = TimeSpan.FromSeconds(1);

ServerSessionTicketStore sut = CreateSut();

await sut.RenewAsync(existingSession.Key, authenticationTicket);

existingSession.Renewed.Should().Be(existingSession.Created);
}

[Fact]
public async Task RenewAsync_WhenOptionalValuesProvided_ShouldUseThem()
{
Expand Down
Loading
Loading