Skip to content

Commit 726f467

Browse files
authored
Obsolete and replace ISystemClock in Security, Identity #47472 (#47717)
1 parent 2e9a018 commit 726f467

File tree

60 files changed

+601
-253
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+601
-253
lines changed

src/DefaultBuilder/test/Microsoft.AspNetCore.Tests/WebApplicationTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2505,7 +2505,7 @@ public async Task UsingCreateSlimBuilderWorksIfRegexConstraintAddedViaAddRouting
25052505

25062506
private class UberHandler : AuthenticationHandler<AuthenticationSchemeOptions>
25072507
{
2508-
public UberHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { }
2508+
public UberHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : base(options, logger, encoder) { }
25092509

25102510
protected override Task HandleChallengeAsync(AuthenticationProperties properties) => Task.CompletedTask;
25112511

src/Identity/Core/src/IdentityBuilderExtensions.cs

+19
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
using System.Diagnostics.CodeAnalysis;
55
using Microsoft.Extensions.DependencyInjection;
6+
using Microsoft.Extensions.DependencyInjection.Extensions;
7+
using Microsoft.Extensions.Options;
68

79
namespace Microsoft.AspNetCore.Identity;
810

@@ -36,6 +38,7 @@ private static void AddSignInManagerDeps(this IdentityBuilder builder)
3638
builder.Services.AddHttpContextAccessor();
3739
builder.Services.AddScoped(typeof(ISecurityStampValidator), typeof(SecurityStampValidator<>).MakeGenericType(builder.UserType));
3840
builder.Services.AddScoped(typeof(ITwoFactorSecurityStampValidator), typeof(TwoFactorSecurityStampValidator<>).MakeGenericType(builder.UserType));
41+
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<SecurityStampValidatorOptions>, PostConfigureSecurityStampValidatorOptions>());
3942
}
4043

4144
/// <summary>
@@ -75,4 +78,20 @@ public static IdentityBuilder AddSignInManager(this IdentityBuilder builder)
7578
builder.Services.AddScoped(managerType, typeof(TSignInManager));
7679
return builder;
7780
}
81+
82+
// Set TimeProvider from DI on all options instances, if not already set by tests.
83+
private sealed class PostConfigureSecurityStampValidatorOptions : IPostConfigureOptions<SecurityStampValidatorOptions>
84+
{
85+
public PostConfigureSecurityStampValidatorOptions(TimeProvider timeProvider)
86+
{
87+
TimeProvider = timeProvider;
88+
}
89+
90+
private TimeProvider TimeProvider { get; }
91+
92+
public void PostConfigure(string? name, SecurityStampValidatorOptions options)
93+
{
94+
options.TimeProvider ??= TimeProvider;
95+
}
96+
}
7897
}

src/Identity/Core/src/IdentityServiceCollectionExtensions.cs

+17
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using Microsoft.AspNetCore.Http;
77
using Microsoft.AspNetCore.Identity;
88
using Microsoft.Extensions.DependencyInjection.Extensions;
9+
using Microsoft.Extensions.Options;
910

1011
namespace Microsoft.Extensions.DependencyInjection;
1112

@@ -92,6 +93,7 @@ public static class IdentityServiceCollectionExtensions
9293
// No interface for the error describer so we can add errors without rev'ing the interface
9394
services.TryAddScoped<IdentityErrorDescriber>();
9495
services.TryAddScoped<ISecurityStampValidator, SecurityStampValidator<TUser>>();
96+
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<SecurityStampValidatorOptions>, PostConfigureSecurityStampValidatorOptions>());
9597
services.TryAddScoped<ITwoFactorSecurityStampValidator, TwoFactorSecurityStampValidator<TUser>>();
9698
services.TryAddScoped<IUserClaimsPrincipalFactory<TUser>, UserClaimsPrincipalFactory<TUser, TRole>>();
9799
services.TryAddScoped<IUserConfirmation<TUser>, DefaultUserConfirmation<TUser>>();
@@ -124,4 +126,19 @@ public static IServiceCollection ConfigureApplicationCookie(this IServiceCollect
124126
/// <returns>The services.</returns>
125127
public static IServiceCollection ConfigureExternalCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
126128
=> services.Configure(IdentityConstants.ExternalScheme, configure);
129+
130+
private sealed class PostConfigureSecurityStampValidatorOptions : IPostConfigureOptions<SecurityStampValidatorOptions>
131+
{
132+
public PostConfigureSecurityStampValidatorOptions(TimeProvider timeProvider)
133+
{
134+
TimeProvider = timeProvider;
135+
}
136+
137+
private TimeProvider TimeProvider { get; }
138+
139+
public void PostConfigure(string? name, SecurityStampValidatorOptions options)
140+
{
141+
options.TimeProvider ??= TimeProvider;
142+
}
143+
}
127144
}
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
#nullable enable
2+
Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.SecurityStampValidator(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions!>! options, Microsoft.AspNetCore.Identity.SignInManager<TUser!>! signInManager, Microsoft.Extensions.Logging.ILoggerFactory! logger) -> void
3+
Microsoft.AspNetCore.Identity.SecurityStampValidator<TUser>.TimeProvider.get -> System.TimeProvider!
4+
Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.TimeProvider.get -> System.TimeProvider?
5+
Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions.TimeProvider.set -> void
6+
Microsoft.AspNetCore.Identity.TwoFactorSecurityStampValidator<TUser>.TwoFactorSecurityStampValidator(Microsoft.Extensions.Options.IOptions<Microsoft.AspNetCore.Identity.SecurityStampValidatorOptions!>! options, Microsoft.AspNetCore.Identity.SignInManager<TUser!>! signInManager, Microsoft.Extensions.Logging.ILoggerFactory! logger) -> void
27
virtual Microsoft.AspNetCore.Identity.SignInManager<TUser>.IsTwoFactorEnabledAsync(TUser! user) -> System.Threading.Tasks.Task<bool>!

src/Identity/Core/src/SecurityStampValidator.cs

+30-7
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,34 @@ public class SecurityStampValidator<TUser> : ISecurityStampValidator where TUser
2323
/// <param name="signInManager">The <see cref="SignInManager{TUser}"/>.</param>
2424
/// <param name="clock">The system clock.</param>
2525
/// <param name="logger">The logger.</param>
26+
[Obsolete("ISystemClock is obsolete, use TimeProvider on SecurityStampValidatorOptions instead.")]
2627
public SecurityStampValidator(IOptions<SecurityStampValidatorOptions> options, SignInManager<TUser> signInManager, ISystemClock clock, ILoggerFactory logger)
2728
{
2829
ArgumentNullException.ThrowIfNull(options);
2930
ArgumentNullException.ThrowIfNull(signInManager);
3031
SignInManager = signInManager;
3132
Options = options.Value;
32-
Clock = clock;
33+
TimeProvider = Options.TimeProvider ?? TimeProvider.System;
34+
Clock = new TimeProviderClock(TimeProvider);
35+
Logger = logger.CreateLogger(GetType());
36+
}
37+
38+
/// <summary>
39+
/// Creates a new instance of <see cref="SecurityStampValidator{TUser}"/>.
40+
/// </summary>
41+
/// <param name="options">Used to access the <see cref="IdentityOptions"/>.</param>
42+
/// <param name="signInManager">The <see cref="SignInManager{TUser}"/>.</param>
43+
/// <param name="logger">The logger.</param>
44+
public SecurityStampValidator(IOptions<SecurityStampValidatorOptions> options, SignInManager<TUser> signInManager, ILoggerFactory logger)
45+
{
46+
ArgumentNullException.ThrowIfNull(options);
47+
ArgumentNullException.ThrowIfNull(signInManager);
48+
SignInManager = signInManager;
49+
Options = options.Value;
50+
TimeProvider = Options.TimeProvider ?? TimeProvider.System;
51+
#pragma warning disable CS0618 // Type or member is obsolete
52+
Clock = new TimeProviderClock(TimeProvider);
53+
#pragma warning restore CS0618 // Type or member is obsolete
3354
Logger = logger.CreateLogger(GetType());
3455
}
3556

@@ -46,8 +67,14 @@ public SecurityStampValidator(IOptions<SecurityStampValidatorOptions> options, S
4667
/// <summary>
4768
/// The <see cref="ISystemClock"/>.
4869
/// </summary>
70+
[Obsolete("ISystemClock is obsolete, use TimeProvider instead.")]
4971
public ISystemClock Clock { get; }
5072

73+
/// <summary>
74+
/// The <see cref="System.TimeProvider"/>.
75+
/// </summary>
76+
public TimeProvider TimeProvider { get; }
77+
5178
/// <summary>
5279
/// Gets the <see cref="ILogger"/> used to log messages.
5380
/// </summary>
@@ -87,7 +114,7 @@ protected virtual async Task SecurityStampVerified(TUser user, CookieValidatePri
87114
{
88115
// On renewal calculate the new ticket length relative to now to avoid
89116
// extending the expiration.
90-
context.Properties.IssuedUtc = Clock.UtcNow;
117+
context.Properties.IssuedUtc = TimeProvider.GetUtcNow();
91118
}
92119
}
93120

@@ -108,11 +135,7 @@ protected virtual async Task SecurityStampVerified(TUser user, CookieValidatePri
108135
/// <returns>The <see cref="Task"/> that represents the asynchronous validation operation.</returns>
109136
public virtual async Task ValidateAsync(CookieValidatePrincipalContext context)
110137
{
111-
var currentUtc = DateTimeOffset.UtcNow;
112-
if (Clock != null)
113-
{
114-
currentUtc = Clock.UtcNow;
115-
}
138+
var currentUtc = TimeProvider.GetUtcNow();
116139
var issuedUtc = context.Properties.IssuedUtc;
117140

118141
// Only validate if enough time has elapsed

src/Identity/Core/src/SecurityStampValidatorOptions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,9 @@ public class SecurityStampValidatorOptions
2020
/// Invoked when the default security stamp validator replaces the user's ClaimsPrincipal in the cookie.
2121
/// </summary>
2222
public Func<SecurityStampRefreshingPrincipalContext, Task>? OnRefreshingPrincipal { get; set; }
23+
24+
/// <summary>
25+
/// Gives control over the timestamps for testing purposes.
26+
/// </summary>
27+
public TimeProvider? TimeProvider { get; set; }
2328
}
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.AspNetCore.Authentication;
5+
6+
/// <summary>
7+
/// Provides access to the normal system clock with precision in seconds.
8+
/// </summary>
9+
#pragma warning disable CS0618 // Type or member is obsolete
10+
internal class TimeProviderClock : ISystemClock
11+
#pragma warning restore CS0618 // Type or member is obsolete
12+
{
13+
private readonly TimeProvider _timeProvider;
14+
15+
internal TimeProviderClock() : this(TimeProvider.System) { }
16+
17+
internal TimeProviderClock(TimeProvider timeProvider)
18+
{
19+
ArgumentNullException.ThrowIfNull(timeProvider);
20+
_timeProvider = timeProvider;
21+
}
22+
23+
public DateTimeOffset UtcNow
24+
{
25+
get
26+
{
27+
// the clock measures whole seconds only, to have integral expires_in results, and
28+
// because milliseconds do not round-trip serialization formats
29+
var utcNowPrecisionSeconds = new DateTime((_timeProvider.GetUtcNow().Ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond, DateTimeKind.Utc);
30+
return new DateTimeOffset(utcNowPrecisionSeconds);
31+
}
32+
}
33+
}

src/Identity/Core/src/TwoFactorSecurityStampValidator.cs

+10
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,19 @@ public class TwoFactorSecurityStampValidator<TUser> : SecurityStampValidator<TUs
2222
/// <param name="signInManager">The <see cref="SignInManager{TUser}"/>.</param>
2323
/// <param name="clock">The system clock.</param>
2424
/// <param name="logger">The logger.</param>
25+
[Obsolete("ISystemClock is obsolete, use TimeProvider on SecurityStampValidatorOptions instead.")]
2526
public TwoFactorSecurityStampValidator(IOptions<SecurityStampValidatorOptions> options, SignInManager<TUser> signInManager, ISystemClock clock, ILoggerFactory logger) : base(options, signInManager, clock, logger)
2627
{ }
2728

29+
/// <summary>
30+
/// Creates a new instance of <see cref="SecurityStampValidator{TUser}"/>.
31+
/// </summary>
32+
/// <param name="options">Used to access the <see cref="IdentityOptions"/>.</param>
33+
/// <param name="signInManager">The <see cref="SignInManager{TUser}"/>.</param>
34+
/// <param name="logger">The logger.</param>
35+
public TwoFactorSecurityStampValidator(IOptions<SecurityStampValidatorOptions> options, SignInManager<TUser> signInManager, ILoggerFactory logger) : base(options, signInManager, logger)
36+
{ }
37+
2838
/// <summary>
2939
/// Verifies the principal's security stamp, returns the matching user if successful
3040
/// </summary>

0 commit comments

Comments
 (0)