// 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);
}
}
}