Add SecurityStampValidator
This commit is contained in:
parent
e74496c079
commit
fc53503d1d
|
|
@ -2,12 +2,14 @@ using Microsoft.AspNet.Builder;
|
||||||
using Microsoft.AspNet.Diagnostics;
|
using Microsoft.AspNet.Diagnostics;
|
||||||
using Microsoft.AspNet.Http;
|
using Microsoft.AspNet.Http;
|
||||||
using Microsoft.AspNet.Identity;
|
using Microsoft.AspNet.Identity;
|
||||||
|
using Microsoft.AspNet.Identity.Authentication;
|
||||||
using Microsoft.AspNet.Routing;
|
using Microsoft.AspNet.Routing;
|
||||||
using Microsoft.AspNet.Security.Cookies;
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
using Microsoft.Data.Entity;
|
using Microsoft.Data.Entity;
|
||||||
using Microsoft.Framework.ConfigurationModel;
|
using Microsoft.Framework.ConfigurationModel;
|
||||||
using Microsoft.Framework.DependencyInjection;
|
using Microsoft.Framework.DependencyInjection;
|
||||||
using IdentitySample.Models;
|
using IdentitySample.Models;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace IdentitySamples
|
namespace IdentitySamples
|
||||||
{
|
{
|
||||||
|
|
@ -65,6 +67,11 @@ namespace IdentitySamples
|
||||||
{
|
{
|
||||||
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
|
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
|
||||||
LoginPath = new PathString("/Account/Login"),
|
LoginPath = new PathString("/Account/Login"),
|
||||||
|
Notifications = new CookieAuthenticationNotifications
|
||||||
|
{
|
||||||
|
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUser>(
|
||||||
|
validateInterval: TimeSpan.FromMinutes(0))
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.UseTwoFactorSignInCookies();
|
app.UseTwoFactorSignInCookies();
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,10 @@
|
||||||
<Content Include="project.json" />
|
<Content Include="project.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="SecurityStampValidator.cs" />
|
||||||
<Compile Include="HttpAuthenticationManager.cs" />
|
<Compile Include="HttpAuthenticationManager.cs" />
|
||||||
<Compile Include="IdentityBuilderExtensions.cs" />
|
<Compile Include="IdentityBuilderExtensions.cs" />
|
||||||
<Compile Include="BuilderExtensions.cs" />
|
<Compile Include="BuilderExtensions.cs" />
|
||||||
<Compile Include="IdentityExtensions.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Principal;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity.Authentication
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static helper class used to configure a CookieAuthenticationProvider to validate a cookie against a user's security
|
||||||
|
/// stamp
|
||||||
|
/// </summary>
|
||||||
|
public static class SecurityStampValidator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Can be used as the ValidateIdentity method for a CookieAuthenticationProvider which will check a user's security
|
||||||
|
/// stamp after validateInterval
|
||||||
|
/// Rejects the identity if the stamp changes, and otherwise will call regenerateIdentity to sign in a new
|
||||||
|
/// ClaimsIdentity
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TUser"></typeparam>
|
||||||
|
/// <param name="validateInterval"></param>
|
||||||
|
/// <param name="regenerateIdentity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TUser>(
|
||||||
|
TimeSpan validateInterval)
|
||||||
|
where TUser : class
|
||||||
|
{
|
||||||
|
return OnValidateIdentity<TUser>(validateInterval, id => id.GetUserId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can be used as the ValidateIdentity method for a CookieAuthenticationProvider which will check a user's security
|
||||||
|
/// stamp after validateInterval
|
||||||
|
/// Rejects the identity if the stamp changes, and otherwise will call regenerateIdentity to sign in a new
|
||||||
|
/// ClaimsIdentity
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TUser"></typeparam>
|
||||||
|
/// <typeparam name="TKey"></typeparam>
|
||||||
|
/// <param name="validateInterval"></param>
|
||||||
|
/// <param name="regenerateIdentityCallback"></param>
|
||||||
|
/// <param name="getUserIdCallback"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static Func<CookieValidateIdentityContext, Task> OnValidateIdentity<TUser>(
|
||||||
|
TimeSpan validateInterval,
|
||||||
|
Func<ClaimsIdentity, string> getUserIdCallback)
|
||||||
|
where TUser : class
|
||||||
|
{
|
||||||
|
return async 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 > validateInterval;
|
||||||
|
}
|
||||||
|
if (validate)
|
||||||
|
{
|
||||||
|
var manager = context.HttpContext.ApplicationServices.GetService<SignInManager<TUser>>();
|
||||||
|
var userId = getUserIdCallback(context.Identity);
|
||||||
|
var user = await manager.ValidateSecurityStamp(context.Identity, userId);
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
bool isPersistent = false;
|
||||||
|
if (context.Properties != null)
|
||||||
|
{
|
||||||
|
isPersistent = context.Properties.IsPersistent;
|
||||||
|
}
|
||||||
|
await manager.SignInAsync(user, isPersistent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.RejectIdentity();
|
||||||
|
manager.SignOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System.Security.Claims;
|
||||||
|
|
||||||
|
namespace System.Security.Principal
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Extensions making it easier to get the user name/user id claims off of an identity
|
||||||
|
/// </summary>
|
||||||
|
public static class ClaimsIdentityExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Return the user name using the UserNameClaimType
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetUserName(this IIdentity identity)
|
||||||
|
{
|
||||||
|
if (identity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("identity");
|
||||||
|
}
|
||||||
|
var ci = identity as ClaimsIdentity;
|
||||||
|
return ci != null ? ci.FindFirstValue(ClaimsIdentity.DefaultNameClaimType) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the user id using the UserIdClaimType
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string GetUserId(this IIdentity identity)
|
||||||
|
{
|
||||||
|
if (identity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("identity");
|
||||||
|
}
|
||||||
|
var ci = identity as ClaimsIdentity;
|
||||||
|
return ci != null ? ci.FindFirstValue(ClaimTypes.NameIdentifier) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Return the claim value for the first claim with the specified type if it exists, null otherwise
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity"></param>
|
||||||
|
/// <param name="claimType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string FindFirstValue(this ClaimsIdentity identity, string claimType)
|
||||||
|
{
|
||||||
|
if (identity == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException("identity");
|
||||||
|
}
|
||||||
|
var claim = identity.FindFirst(claimType);
|
||||||
|
return claim != null ? claim.Value : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
<Content Include="Resources.resx" />
|
<Content Include="Resources.resx" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="ClaimsIdentityExtensions.cs" />
|
||||||
<Compile Include="ClaimsIdentityFactory.cs" />
|
<Compile Include="ClaimsIdentityFactory.cs" />
|
||||||
<Compile Include="ClaimsIdentityOptions.cs" />
|
<Compile Include="ClaimsIdentityOptions.cs" />
|
||||||
<Compile Include="Crypto.cs" />
|
<Compile Include="Crypto.cs" />
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Microsoft.AspNet.Identity
|
namespace Microsoft.AspNet.Identity
|
||||||
|
|
@ -51,12 +52,34 @@ namespace Microsoft.AspNet.Identity
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Should this be async?
|
// TODO: Should this be async?
|
||||||
public void SignOut()
|
public virtual void SignOut()
|
||||||
{
|
{
|
||||||
// REVIEW: need a new home for this option config?
|
// REVIEW: need a new home for this option config?
|
||||||
AuthenticationManager.SignOut(UserManager.Options.ClaimsIdentity.AuthenticationType);
|
AuthenticationManager.SignOut(UserManager.Options.ClaimsIdentity.AuthenticationType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validates that the claims identity has a security stamp matching the users
|
||||||
|
/// Returns the user if it matches, null otherwise
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="identity"></param>
|
||||||
|
/// <param name="userId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task<TUser> ValidateSecurityStamp(ClaimsIdentity identity, string userId)
|
||||||
|
{
|
||||||
|
var user = await UserManager.FindByIdAsync(userId);
|
||||||
|
if (user != null && UserManager.SupportsUserSecurityStamp)
|
||||||
|
{
|
||||||
|
var securityStamp =
|
||||||
|
identity.FindFirstValue(UserManager.Options.ClaimsIdentity.SecurityStampClaimType);
|
||||||
|
if (securityStamp == await UserManager.GetSecurityStampAsync(user))
|
||||||
|
{
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password,
|
public virtual async Task<SignInStatus> PasswordSignInAsync(string userName, string password,
|
||||||
bool isPersistent, bool shouldLockout)
|
bool isPersistent, bool shouldLockout)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="HttpSignInTest.cs" />
|
<Compile Include="HttpSignInTest.cs" />
|
||||||
<Compile Include="IdentityExtensionsTest.cs" />
|
<Compile Include="SecurityStampValidatorTest.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||||
</Project>
|
</Project>
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Http;
|
||||||
|
using Microsoft.AspNet.Http.Security;
|
||||||
|
using Microsoft.AspNet.Identity.Test;
|
||||||
|
using Microsoft.AspNet.Security;
|
||||||
|
using Microsoft.AspNet.Security.Cookies;
|
||||||
|
using Microsoft.Framework.DependencyInjection;
|
||||||
|
using Microsoft.Framework.DependencyInjection.Fallback;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity.Authentication.Test
|
||||||
|
{
|
||||||
|
public class SecurityStampTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task OnValidateIdentityThrowsWithEmptyServiceCollection()
|
||||||
|
{
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
httpContext.Setup(c => c.ApplicationServices).Returns(new ServiceCollection().BuildServiceProvider());
|
||||||
|
var id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
|
||||||
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
||||||
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
|
await Assert.ThrowsAsync<Exception>(() => SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public async Task OnValidateIdentityTestSuccess(bool isPersistent)
|
||||||
|
{
|
||||||
|
var user = new IdentityUser("test");
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
|
var authManager = new Mock<IAuthenticationManager>();
|
||||||
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
|
authManager.Object, claimsManager.Object);
|
||||||
|
signInManager.Setup(s => s.ValidateSecurityStamp(It.IsAny<ClaimsIdentity>(), user.Id)).ReturnsAsync(user).Verifiable();
|
||||||
|
signInManager.Setup(s => s.SignInAsync(user, isPersistent)).Returns(Task.FromResult(0)).Verifiable();
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(signInManager.Object);
|
||||||
|
httpContext.Setup(c => c.ApplicationServices).Returns(services.BuildServiceProvider());
|
||||||
|
var id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
|
||||||
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
||||||
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, IsPersistent = isPersistent });
|
||||||
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
|
Assert.NotNull(context.Properties);
|
||||||
|
Assert.NotNull(context.Options);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
await
|
||||||
|
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
signInManager.VerifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails()
|
||||||
|
{
|
||||||
|
var user = new IdentityUser("test");
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
|
var authManager = new Mock<IAuthenticationManager>();
|
||||||
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
|
authManager.Object, claimsManager.Object);
|
||||||
|
signInManager.Setup(s => s.ValidateSecurityStamp(It.IsAny<ClaimsIdentity>(), user.Id)).ReturnsAsync(null).Verifiable();
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(signInManager.Object);
|
||||||
|
httpContext.Setup(c => c.ApplicationServices).Returns(services.BuildServiceProvider());
|
||||||
|
var id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
|
||||||
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
||||||
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
||||||
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
|
Assert.NotNull(context.Properties);
|
||||||
|
Assert.NotNull(context.Options);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
await
|
||||||
|
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||||
|
Assert.Null(context.Identity);
|
||||||
|
signInManager.VerifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OnValidateIdentityRejectsWhenNoIssuedUtc()
|
||||||
|
{
|
||||||
|
var user = new IdentityUser("test");
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
|
var authManager = new Mock<IAuthenticationManager>();
|
||||||
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
|
authManager.Object, claimsManager.Object);
|
||||||
|
signInManager.Setup(s => s.ValidateSecurityStamp(It.IsAny<ClaimsIdentity>(), user.Id)).ReturnsAsync(null).Verifiable();
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(signInManager.Object);
|
||||||
|
httpContext.Setup(c => c.ApplicationServices).Returns(services.BuildServiceProvider());
|
||||||
|
var id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
|
||||||
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
||||||
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties());
|
||||||
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
|
Assert.NotNull(context.Properties);
|
||||||
|
Assert.NotNull(context.Options);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
await
|
||||||
|
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.Zero).Invoke(context);
|
||||||
|
Assert.Null(context.Identity);
|
||||||
|
signInManager.VerifyAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired()
|
||||||
|
{
|
||||||
|
var user = new IdentityUser("test");
|
||||||
|
var httpContext = new Mock<HttpContext>();
|
||||||
|
var userManager = MockHelpers.MockUserManager<IdentityUser>();
|
||||||
|
var authManager = new Mock<IAuthenticationManager>();
|
||||||
|
var claimsManager = new Mock<IClaimsIdentityFactory<IdentityUser>>();
|
||||||
|
var signInManager = new Mock<SignInManager<IdentityUser>>(userManager.Object,
|
||||||
|
authManager.Object, claimsManager.Object);
|
||||||
|
signInManager.Setup(s => s.ValidateSecurityStamp(It.IsAny<ClaimsIdentity>(), user.Id)).Throws(new Exception("Shouldn't be called"));
|
||||||
|
signInManager.Setup(s => s.SignInAsync(user, false)).Throws(new Exception("Shouldn't be called"));
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
services.AddInstance(signInManager.Object);
|
||||||
|
httpContext.Setup(c => c.ApplicationServices).Returns(services.BuildServiceProvider());
|
||||||
|
var id = new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie);
|
||||||
|
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
|
||||||
|
|
||||||
|
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow });
|
||||||
|
var context = new CookieValidateIdentityContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
|
||||||
|
Assert.NotNull(context.Properties);
|
||||||
|
Assert.NotNull(context.Options);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
await
|
||||||
|
SecurityStampValidator.OnValidateIdentity<IdentityUser>(TimeSpan.FromDays(1)).Invoke(context);
|
||||||
|
Assert.NotNull(context.Identity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (c) Microsoft Open Technologies, Inc. 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.Security.Principal;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNet.Identity.Test
|
||||||
|
{
|
||||||
|
public class ClaimsIdentityExtensionsTest
|
||||||
|
{
|
||||||
|
public const string ExternalAuthenticationType = "TestExternalAuth";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IdentityNullCheckTest()
|
||||||
|
{
|
||||||
|
IIdentity identity = null;
|
||||||
|
Assert.Throws<ArgumentNullException>("identity", () => identity.GetUserId());
|
||||||
|
Assert.Throws<ArgumentNullException>("identity", () => identity.GetUserName());
|
||||||
|
ClaimsIdentity claimsIdentity = null;
|
||||||
|
Assert.Throws<ArgumentNullException>("identity", () => claimsIdentity.FindFirstValue(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IdentityNullIfNotClaimsIdentityTest()
|
||||||
|
{
|
||||||
|
IIdentity identity = new TestIdentity();
|
||||||
|
Assert.Null(identity.GetUserId());
|
||||||
|
Assert.Null(identity.GetUserName());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void UserNameAndIdTest()
|
||||||
|
{
|
||||||
|
var id = CreateTestExternalIdentity();
|
||||||
|
Assert.Equal("NameIdentifier", id.GetUserId());
|
||||||
|
Assert.Equal("Name", id.GetUserName());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void IdentityExtensionsFindFirstValueNullIfUnknownTest()
|
||||||
|
{
|
||||||
|
var id = CreateTestExternalIdentity();
|
||||||
|
Assert.Null(id.FindFirstValue("bogus"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ClaimsIdentity CreateTestExternalIdentity()
|
||||||
|
{
|
||||||
|
return new ClaimsIdentity(
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new Claim(ClaimTypes.NameIdentifier, "NameIdentifier", null, ExternalAuthenticationType),
|
||||||
|
new Claim(ClaimTypes.Name, "Name")
|
||||||
|
},
|
||||||
|
ExternalAuthenticationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestIdentity : IIdentity
|
||||||
|
{
|
||||||
|
public string AuthenticationType
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsAuthenticated
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ClaimsIdentityFactoryTest.cs" />
|
<Compile Include="ClaimsIdentityFactoryTest.cs" />
|
||||||
<Compile Include="IdentityBuilderTest.cs" />
|
<Compile Include="IdentityBuilderTest.cs" />
|
||||||
|
<Compile Include="ClaimsIdentityExtensionsTest.cs" />
|
||||||
<Compile Include="IdentityOptionsTest.cs" />
|
<Compile Include="IdentityOptionsTest.cs" />
|
||||||
<Compile Include="IdentityResultTest.cs" />
|
<Compile Include="IdentityResultTest.cs" />
|
||||||
<Compile Include="NoopRoleStore.cs" />
|
<Compile Include="NoopRoleStore.cs" />
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue