// 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.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; namespace Microsoft.AspNet.Identity { /// /// Provides default implementation of validation functions for security stamps. /// /// The type encapsulating a user. public class SecurityStampValidator : ISecurityStampValidator where TUser : class { /// /// 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 manager = context.HttpContext.RequestServices.GetRequiredService>(); var userId = context.Principal.GetUserId(); var user = await manager.ValidateSecurityStampAsync(context.Principal, userId); if (user != null) { // REVIEW: note we lost login authenticaiton method context.ReplacePrincipal(await manager.CreateUserPrincipalAsync(user)); context.ShouldRenew = true; } else { context.RejectPrincipal(); await manager.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) { var currentUtc = DateTimeOffset.UtcNow; if (context.Options != null && context.Options.SystemClock != null) { currentUtc = context.Options.SystemClock.UtcNow; } var issuedUtc = context.Properties.IssuedUtc; if (context.HttpContext.RequestServices == null) { throw new InvalidOperationException("TODO: RequestServices is null, missing Use[Request]Services?"); } // Only validate if enough time has elapsed var validate = (issuedUtc == null); if (issuedUtc != null) { var timeElapsed = currentUtc.Subtract(issuedUtc.Value); var accessor = context.HttpContext.RequestServices.GetRequiredService>(); validate = timeElapsed > accessor.Value.SecurityStampValidationInterval; } if (validate) { var validator = context.HttpContext.RequestServices.GetRequiredService(); return validator.ValidateAsync(context); } return Task.FromResult(0); } } }