// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Identity { /// /// Provides default implementation of validation functions for security stamps. /// /// The type encapsulating a user. public class SecurityStampValidator : ISecurityStampValidator where TUser : class { private readonly SignInManager _signInManager; private readonly IdentityOptions _options; public SecurityStampValidator(IOptions options, SignInManager signInManager) { if (options == null) { throw new ArgumentNullException(nameof(options)); } if (signInManager == null) { throw new ArgumentNullException(nameof(signInManager)); } _signInManager = signInManager; _options = options.Value; } /// /// Validates a security stamp of an identity as an asynchronous operation, and rebuilds the identity if the validation succeeds, otherwise rejects /// the identity. /// /// The context containing the /// and to validate. /// The that represents the asynchronous validation operation. public virtual async Task ValidateAsync(CookieValidatePrincipalContext context) { var currentUtc = DateTimeOffset.UtcNow; if (context.Options != null && context.Options.SystemClock != null) { currentUtc = context.Options.SystemClock.UtcNow; } var issuedUtc = context.Properties.IssuedUtc; // Only validate if enough time has elapsed var validate = (issuedUtc == null); if (issuedUtc != null) { var timeElapsed = currentUtc.Subtract(issuedUtc.Value); validate = timeElapsed > _options.SecurityStampValidationInterval; } if (validate) { var user = await _signInManager.ValidateSecurityStampAsync(context.Principal); if (user != null) { // REVIEW: note we lost login authenticaiton method context.ReplacePrincipal(await _signInManager.CreateUserPrincipalAsync(user)); context.ShouldRenew = true; } else { context.RejectPrincipal(); await _signInManager.SignOutAsync(); } } } } /// /// Static helper class used to configure a CookieAuthenticationNotifications to validate a cookie against a user's security /// stamp. /// public static class SecurityStampValidator { /// /// Validates a principal against a user's stored security stamp. /// the identity. /// /// The context containing the /// and to validate. /// The that represents the asynchronous validation operation. public static Task ValidatePrincipalAsync(CookieValidatePrincipalContext context) { if (context.HttpContext.RequestServices == null) { throw new InvalidOperationException("RequestServices is null."); } var validator = context.HttpContext.RequestServices.GetRequiredService(); return validator.ValidateAsync(context); } } }