Identity => Auth 2.0

This commit is contained in:
Hao Kung 2017-04-19 11:55:25 -07:00
parent d4669453ba
commit 14b4bea964
22 changed files with 396 additions and 372 deletions

View File

@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging;
using IdentitySample.Models;
using IdentitySample.Models.ManageViewModels;
using IdentitySample.Services;
using Microsoft.AspNetCore.Authentication;
namespace IdentitySamples.Controllers
{
@ -19,6 +20,7 @@ namespace IdentitySamples.Controllers
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IAuthenticationSchemeProvider _schemes;
private readonly IEmailSender _emailSender;
private readonly ISmsSender _smsSender;
private readonly ILogger _logger;
@ -26,12 +28,14 @@ namespace IdentitySamples.Controllers
public ManageController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,
IAuthenticationSchemeProvider schemes,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory)
{
_userManager = userManager;
_signInManager = signInManager;
_schemes = schemes;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<ManageController>();
@ -308,7 +312,8 @@ namespace IdentitySamples.Controllers
return View("Error");
}
var userLogins = await _userManager.GetLoginsAsync(user);
var otherLogins = _signInManager.GetExternalAuthenticationSchemes().Where(auth => userLogins.All(ul => auth.AuthenticationScheme != ul.LoginProvider)).ToList();
var schemes = await _schemes.GetAllSchemesAsync();
var otherLogins = schemes.Where(auth => userLogins.All(ul => auth.Name != ul.LoginProvider)).ToList();
ViewData["ShowRemoveButton"] = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
namespace IdentitySample.Models.ManageViewModels
@ -11,6 +8,6 @@ namespace IdentitySample.Models.ManageViewModels
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
public IList<AuthenticationScheme> OtherLogins { get; set; }
}
}

View File

@ -68,7 +68,6 @@ namespace IdentitySample
app.UseStaticFiles();
app.UseIdentity();
// To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>

View File

@ -1,8 +1,9 @@
@using System.Collections.Generic
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Authentication
@using Microsoft.AspNetCore.Authentication
@model LoginViewModel
@inject SignInManager<ApplicationUser> SignInManager
@inject IAuthenticationSchemeProvider SchemeProvider
@{
ViewData["Title"] = "Log in";
@ -59,7 +60,8 @@
<h4>Use another service to log in.</h4>
<hr />
@{
var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList();
var schemes = await SchemeProvider.GetAllSchemesAsync();
var loginProviders = schemes.ToList();
if (loginProviders.Count == 0)
{
<div>
@ -76,7 +78,7 @@
<p>
@foreach (var provider in loginProviders)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.Name account">@provider.Name</button>
}
</p>
</div>

View File

@ -46,7 +46,7 @@
<p>
@foreach (var provider in Model.OtherLogins)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.AuthenticationScheme" title="Log in using your @provider.DisplayName account">@provider.AuthenticationScheme</button>
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.Name account">@provider.Name</button>
}
</p>
</div>

View File

@ -10,6 +10,7 @@ using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -58,6 +59,7 @@ namespace Microsoft.AspNetCore.Identity.Test
/// <param name="context"></param>
protected virtual void SetupIdentityServices(IServiceCollection services, object context = null)
{
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddIdentity<TUser, TRole>(options =>
{

View File

@ -1,12 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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 Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder
{
/// <summary>
@ -19,25 +15,7 @@ namespace Microsoft.AspNetCore.Builder
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> instance this method extends.</param>
/// <returns>The <see cref="IApplicationBuilder"/> instance this method extends.</returns>
public static IApplicationBuilder UseIdentity(this IApplicationBuilder app)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var marker = app.ApplicationServices.GetService<IdentityMarkerService>();
if (marker == null)
{
throw new InvalidOperationException(Resources.MustCallAddIdentity);
}
var options = app.ApplicationServices.GetRequiredService<IOptions<IdentityOptions>>().Value;
app.UseCookieAuthentication(options.Cookies.ExternalCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorRememberMeCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorUserIdCookie);
app.UseCookieAuthentication(options.Cookies.ApplicationCookie);
return app;
}
[Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)]
public static IApplicationBuilder UseIdentity(this IApplicationBuilder app) => app;
}
}

View File

@ -3,8 +3,6 @@
using System;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Identity
{
@ -14,95 +12,72 @@ namespace Microsoft.AspNetCore.Identity
public class IdentityCookieOptions
{
private static readonly string CookiePrefix = "Identity";
private static readonly string DefaultApplicationScheme = CookiePrefix + ".Application";
private static readonly string DefaultExternalScheme = CookiePrefix + ".External";
private static readonly string DefaultTwoFactorRememberMeScheme = CookiePrefix + ".TwoFactorRememberMe";
private static readonly string DefaultTwoFactorUserIdScheme = CookiePrefix + ".TwoFactorUserId";
/// <summary>
/// The scheme used to identify application authentication cookies.
/// </summary>
public static readonly string ApplicationScheme = CookiePrefix + ".Application";
/// <summary>
/// Constructs a new instance of <see cref="IdentityCookieOptions"/>.
/// The scheme used to identify external authentication cookies.
/// </summary>
public IdentityCookieOptions()
{
// Configure all of the cookie middlewares
ApplicationCookie = new CookieAuthenticationOptions
{
AuthenticationScheme = DefaultApplicationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = new PathString("/Account/Login"),
Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
}
};
public static readonly string ExternalScheme = CookiePrefix + ".External";
ExternalCookie = new CookieAuthenticationOptions
{
AutomaticAuthenticate = false,
AuthenticationScheme = DefaultExternalScheme,
CookieName = DefaultExternalScheme,
ExpireTimeSpan = TimeSpan.FromMinutes(5)
};
/// <summary>
/// The scheme used to identify Two Factor authentication cookies for saving the Remember Me state.
/// </summary>
public static readonly string TwoFactorRememberMeScheme = CookiePrefix + ".TwoFactorRememberMe";
TwoFactorRememberMeCookie = new CookieAuthenticationOptions
{
AutomaticAuthenticate = false,
AuthenticationScheme = DefaultTwoFactorRememberMeScheme,
CookieName = DefaultTwoFactorRememberMeScheme
};
TwoFactorUserIdCookie = new CookieAuthenticationOptions
{
AutomaticAuthenticate = false,
AuthenticationScheme = DefaultTwoFactorUserIdScheme,
CookieName = DefaultTwoFactorUserIdScheme,
ExpireTimeSpan = TimeSpan.FromMinutes(5)
};
}
/// <summary>
/// The scheme used to identify Two Factor authentication cookies for round tripping user identities.
/// </summary>
public static readonly string TwoFactorUserIdScheme = CookiePrefix + ".TwoFactorUserId";
/// <summary>
/// The options for the application cookie.
/// </summary>
[Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)]
public CookieAuthenticationOptions ApplicationCookie { get; set; }
/// <summary>
/// The options for the external cookie.
/// </summary>
[Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)]
public CookieAuthenticationOptions ExternalCookie { get; set; }
/// <summary>
/// The options for the two factor remember me cookie.
/// </summary>
[Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)]
public CookieAuthenticationOptions TwoFactorRememberMeCookie { get; set; }
/// <summary>
/// The options for the two factor user id cookie.
/// </summary>
[Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)]
public CookieAuthenticationOptions TwoFactorUserIdCookie { get; set; }
/// <summary>
/// Gets the scheme used to identify application authentication cookies.
/// </summary>
/// <value>The scheme used to identify application authentication cookies.</value>
public string ApplicationCookieAuthenticationScheme => ApplicationCookie?.AuthenticationScheme;
public string ApplicationCookieAuthenticationScheme { get; set; } = ApplicationScheme;
/// <summary>
/// Gets the scheme used to identify external authentication cookies.
/// </summary>
/// <value>The scheme used to identify external authentication cookies.</value>
public string ExternalCookieAuthenticationScheme => ExternalCookie?.AuthenticationScheme;
public string ExternalCookieAuthenticationScheme { get; set; } = ExternalScheme;
/// <summary>
/// Gets the scheme used to identify Two Factor authentication cookies for round tripping user identities.
/// </summary>
/// <value>The scheme used to identify user identity 2fa authentication cookies.</value>
public string TwoFactorUserIdCookieAuthenticationScheme => TwoFactorUserIdCookie?.AuthenticationScheme;
public string TwoFactorUserIdCookieAuthenticationScheme { get; set; } = TwoFactorUserIdScheme;
/// <summary>
/// Gets the scheme used to identify Two Factor authentication cookies for saving the Remember Me state.
/// </summary>
/// <value>The scheme used to identify remember me application authentication cookies.</value>
public string TwoFactorRememberMeCookieAuthenticationScheme => TwoFactorRememberMeCookie?.AuthenticationScheme;
public string TwoFactorRememberMeCookieAuthenticationScheme { get; set; } = TwoFactorRememberMeScheme;
}
}

View File

@ -1,10 +0,0 @@
// 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.
namespace Microsoft.AspNetCore.Identity
{
/// <summary>
/// Used to verify AddIdentity was called on a ServiceCollection
/// </summary>
public class IdentityMarkerService { }
}

View File

@ -2,10 +2,13 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
@ -14,6 +17,13 @@ namespace Microsoft.Extensions.DependencyInjection
/// </summary>
public static class IdentityServiceCollectionExtensions
{
internal class IdentityConfigureOptions : ConfigureOptions<IdentityOptions>
{
public IdentityConfigureOptions(IConfiguration config) :
base(options => config.GetSection("Identity").Bind(options))
{ }
}
/// <summary>
/// Adds the default identity system configuration for the specified User and Role types.
/// </summary>
@ -29,6 +39,24 @@ namespace Microsoft.Extensions.DependencyInjection
return services.AddIdentity<TUser, TRole>(setupAction: null);
}
/// <summary>
/// Configures the application cookie.
/// </summary>
/// <param name="services">The services available in the application.</param>
/// <param name="configure">An action to configure the <see cref="CookieAuthenticationOptions"/>.</param>
/// <returns>The services.</returns>
public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
=> services.Configure(IdentityCookieOptions.ApplicationScheme, configure);
/// <summary>
/// Configure the external cookie.
/// </summary>
/// <param name="services">The services available in the application.</param>
/// <param name="configure">An action to configure the <see cref="CookieAuthenticationOptions"/>.</param>
/// <returns>The services.</returns>
public static IServiceCollection ConfigureExternalCookie(this IServiceCollection services, Action<CookieAuthenticationOptions> configure)
=> services.Configure(IdentityCookieOptions.ExternalScheme, configure);
/// <summary>
/// Adds and configures the identity system for the specified User and Role types.
/// </summary>
@ -44,16 +72,40 @@ namespace Microsoft.Extensions.DependencyInjection
where TRole : class
{
// Services used by identity
services.AddAuthentication(options =>
services.AddAuthenticationCore(options =>
{
// This is the Default value for ExternalCookieAuthenticationScheme
options.SignInScheme = new IdentityCookieOptions().ExternalCookieAuthenticationScheme;
options.DefaultAuthenticateScheme = IdentityCookieOptions.ApplicationScheme;
options.DefaultChallengeScheme = IdentityCookieOptions.ApplicationScheme;
options.DefaultSignInScheme = IdentityCookieOptions.ExternalScheme;
});
services.AddCookieAuthentication(IdentityCookieOptions.ApplicationScheme, o =>
{
o.LoginPath = new PathString("/Account/Login");
o.Events = new CookieAuthenticationEvents
{
OnValidatePrincipal = SecurityStampValidator.ValidatePrincipalAsync
};
});
services.AddCookieAuthentication(IdentityCookieOptions.ExternalScheme, o =>
{
o.CookieName = IdentityCookieOptions.ExternalScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
services.AddCookieAuthentication(IdentityCookieOptions.TwoFactorRememberMeScheme,
o => o.CookieName = IdentityCookieOptions.TwoFactorRememberMeScheme);
services.AddCookieAuthentication(IdentityCookieOptions.TwoFactorUserIdScheme, o =>
{
o.CookieName = IdentityCookieOptions.TwoFactorUserIdScheme;
o.ExpireTimeSpan = TimeSpan.FromMinutes(5);
});
// Hosting doesn't add IHttpContextAccessor by default
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Identity services
services.TryAddSingleton<IdentityMarkerService>();
services.TryAddScoped<IUserValidator<TUser>, UserValidator<TUser>>();
services.TryAddScoped<IPasswordValidator<TUser>, PasswordValidator<TUser>>();
services.TryAddScoped<IPasswordHasher<TUser>, PasswordHasher<TUser>>();
@ -67,6 +119,7 @@ namespace Microsoft.Extensions.DependencyInjection
services.TryAddScoped<SignInManager<TUser>, SignInManager<TUser>>();
services.TryAddScoped<RoleManager<TRole>, RoleManager<TRole>>();
services.AddSingleton<IConfigureOptions<IdentityOptions>, IdentityConfigureOptions>();
if (setupAction != null)
{
services.Configure(setupAction);

View File

@ -13,6 +13,7 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.Extensions.TaskCache.Sources" Version="$(AspNetCoreVersion)" PrivateAssets="All" />
<PackageReference Include="System.ComponentModel.Annotations" Version="$(CoreFxVersion)" />
</ItemGroup>

View File

@ -3,6 +3,7 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
@ -18,13 +19,15 @@ namespace Microsoft.AspNetCore.Identity
{
private readonly SignInManager<TUser> _signInManager;
private readonly IdentityOptions _options;
private ISystemClock _clock;
/// <summary>
/// Creates a new instance of <see cref="SecurityStampValidator{TUser}"/>.
/// </summary>
/// <param name="options">Used to access the <see cref="IdentityOptions"/>.</param>
/// <param name="signInManager">The <see cref="SignInManager{TUser}"/>.</param>
public SecurityStampValidator(IOptions<IdentityOptions> options, SignInManager<TUser> signInManager)
/// <param name="clock">The system clock.</param>
public SecurityStampValidator(IOptions<IdentityOptions> options, SignInManager<TUser> signInManager, ISystemClock clock)
{
if (options == null)
{
@ -36,6 +39,7 @@ namespace Microsoft.AspNetCore.Identity
}
_signInManager = signInManager;
_options = options.Value;
_clock = clock;
}
/// <summary>
@ -48,9 +52,9 @@ namespace Microsoft.AspNetCore.Identity
public virtual async Task ValidateAsync(CookieValidatePrincipalContext context)
{
var currentUtc = DateTimeOffset.UtcNow;
if (context.Options != null && context.Options.SystemClock != null)
if (context.Options != null && _clock != null)
{
currentUtc = context.Options.SystemClock.UtcNow;
currentUtc = _clock.UtcNow;
}
var issuedUtc = context.Properties.IssuedUtc;

View File

@ -2,15 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -159,10 +156,9 @@ namespace Microsoft.AspNetCore.Identity
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual async Task RefreshSignInAsync(TUser user)
{
var auth = new AuthenticateContext(Options.Cookies.ApplicationCookieAuthenticationScheme);
await Context.Authentication.AuthenticateAsync(auth);
var authenticationMethod = auth.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
await SignInAsync(user, new AuthenticationProperties(auth.Properties), authenticationMethod);
var auth = await Context.AuthenticateAsync(Options.Cookies.ApplicationCookieAuthenticationScheme);
var authenticationMethod = auth?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
await SignInAsync(user, auth?.Properties, authenticationMethod);
}
/// <summary>
@ -192,7 +188,7 @@ namespace Microsoft.AspNetCore.Identity
{
userPrincipal.Identities.First().AddClaim(new Claim(ClaimTypes.AuthenticationMethod, authenticationMethod));
}
await Context.Authentication.SignInAsync(Options.Cookies.ApplicationCookieAuthenticationScheme,
await Context.SignInAsync(Options.Cookies.ApplicationCookieAuthenticationScheme,
userPrincipal,
authenticationProperties ?? new AuthenticationProperties());
}
@ -202,9 +198,9 @@ namespace Microsoft.AspNetCore.Identity
/// </summary>
public virtual async Task SignOutAsync()
{
await Context.Authentication.SignOutAsync(Options.Cookies.ApplicationCookieAuthenticationScheme);
await Context.Authentication.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
await Context.Authentication.SignOutAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.ApplicationCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
}
/// <summary>
@ -333,8 +329,8 @@ namespace Microsoft.AspNetCore.Identity
public virtual async Task<bool> IsTwoFactorClientRememberedAsync(TUser user)
{
var userId = await UserManager.GetUserIdAsync(user);
var result = await Context.Authentication.AuthenticateAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
return (result != null && result.FindFirstValue(ClaimTypes.Name) == userId);
var result = await Context.AuthenticateAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
return (result?.Principal != null && result.Principal.FindFirstValue(ClaimTypes.Name) == userId);
}
/// <summary>
@ -348,7 +344,7 @@ namespace Microsoft.AspNetCore.Identity
var userId = await UserManager.GetUserIdAsync(user);
var rememberBrowserIdentity = new ClaimsIdentity(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
rememberBrowserIdentity.AddClaim(new Claim(ClaimTypes.Name, userId));
await Context.Authentication.SignInAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme,
await Context.SignInAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme,
new ClaimsPrincipal(rememberBrowserIdentity),
new AuthenticationProperties { IsPersistent = true });
}
@ -359,7 +355,7 @@ namespace Microsoft.AspNetCore.Identity
/// <returns>The task object representing the asynchronous operation.</returns>
public virtual Task ForgetTwoFactorClientAsync()
{
return Context.Authentication.SignOutAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
return Context.SignOutAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
}
/// <summary>
@ -399,10 +395,10 @@ namespace Microsoft.AspNetCore.Identity
// Cleanup external cookie
if (twoFactorInfo.LoginProvider != null)
{
await Context.Authentication.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
}
// Cleanup two factor user id cookie
await Context.Authentication.SignOutAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
if (rememberClient)
{
await RememberTwoFactorClientAsync(user);
@ -539,15 +535,6 @@ namespace Microsoft.AspNetCore.Identity
return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor);
}
/// <summary>
/// Gets a collection of <see cref="AuthenticationDescription"/>s for the known external login providers.
/// </summary>
/// <returns>A collection of <see cref="AuthenticationDescription"/>s for the known external login providers.</returns>
public virtual IEnumerable<AuthenticationDescription> GetExternalAuthenticationSchemes()
{
return Context.Authentication.GetAuthenticationSchemes().Where(d => !string.IsNullOrEmpty(d.DisplayName));
}
/// <summary>
/// Gets the external login information for the current login, as an asynchronous operation.
/// </summary>
@ -556,20 +543,20 @@ namespace Microsoft.AspNetCore.Identity
/// for the sign-in attempt.</returns>
public virtual async Task<ExternalLoginInfo> GetExternalLoginInfoAsync(string expectedXsrf = null)
{
var auth = new AuthenticateContext(Options.Cookies.ExternalCookieAuthenticationScheme);
await Context.Authentication.AuthenticateAsync(auth);
if (auth.Principal == null || auth.Properties == null || !auth.Properties.ContainsKey(LoginProviderKey))
var auth = await Context.AuthenticateAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
var items = auth?.Properties?.Items;
if (auth?.Principal == null || items == null || !items.ContainsKey(LoginProviderKey))
{
return null;
}
if (expectedXsrf != null)
{
if (!auth.Properties.ContainsKey(XsrfKey))
if (!items.ContainsKey(XsrfKey))
{
return null;
}
var userId = auth.Properties[XsrfKey] as string;
var userId = items[XsrfKey] as string;
if (userId != expectedXsrf)
{
return null;
@ -577,14 +564,15 @@ namespace Microsoft.AspNetCore.Identity
}
var providerKey = auth.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var provider = auth.Properties[LoginProviderKey] as string;
var provider = items[LoginProviderKey] as string;
if (providerKey == null || provider == null)
{
return null;
}
return new ExternalLoginInfo(auth.Principal, provider, providerKey, new AuthenticationDescription(auth.Description).DisplayName)
// TODO: display name gone?. Add [] indexer for Authproperties
return new ExternalLoginInfo(auth.Principal, provider, providerKey, provider)
{
AuthenticationTokens = new AuthenticationProperties(auth.Properties).GetTokens()
AuthenticationTokens = auth.Properties.GetTokens()
};
}
@ -682,14 +670,14 @@ namespace Microsoft.AspNetCore.Identity
{
// Store the userId for use after two factor check
var userId = await UserManager.GetUserIdAsync(user);
await Context.Authentication.SignInAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, StoreTwoFactorInfo(userId, loginProvider));
await Context.SignInAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, StoreTwoFactorInfo(userId, loginProvider));
return SignInResult.TwoFactorRequired;
}
}
// Cleanup external cookie
if (loginProvider != null)
{
await Context.Authentication.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
await Context.SignOutAsync(Options.Cookies.ExternalCookieAuthenticationScheme);
}
await SignInAsync(user, isPersistent, loginProvider);
return SignInResult.Success;
@ -697,13 +685,13 @@ namespace Microsoft.AspNetCore.Identity
private async Task<TwoFactorAuthenticationInfo> RetrieveTwoFactorInfoAsync()
{
var result = await Context.Authentication.AuthenticateAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
if (result != null)
var result = await Context.AuthenticateAsync(Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme);
if (result?.Principal != null)
{
return new TwoFactorAuthenticationInfo
{
UserId = result.FindFirstValue(ClaimTypes.Name),
LoginProvider = result.FindFirstValue(ClaimTypes.AuthenticationMethod)
UserId = result.Principal.FindFirstValue(ClaimTypes.Name),
LoginProvider = result.Principal.FindFirstValue(ClaimTypes.AuthenticationMethod)
};
}
return null;

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
@ -24,6 +25,7 @@ namespace Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test
var services = new ServiceCollection();
services
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.AddDbContext<IdentityDbContext>(o => o.UseSqlServer(fixture.ConnectionString))
.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<IdentityDbContext>();

View File

@ -1,21 +1,16 @@
// 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.Diagnostics;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder.Internal;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.AspNetCore.Identity.InMemory.Test
{
@ -26,25 +21,25 @@ namespace Microsoft.AspNetCore.Identity.InMemory.Test
[InlineData(false)]
public async Task VerifyAccountControllerSignIn(bool isPersistent)
{
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
auth.Setup(a => a.SignInAsync(new IdentityCookieOptions().ApplicationCookieAuthenticationScheme,
var context = new DefaultHttpContext();
var auth = MockAuth(context);
auth.Setup(a => a.SignInAsync(context, new IdentityCookieOptions().ApplicationCookieAuthenticationScheme,
It.IsAny<ClaimsPrincipal>(),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
// REVIEW: is persistant mocking broken
//It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Returns(Task.FromResult(0)).Verifiable();
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton(contextAccessor.Object);
contextAccessor.Setup(a => a.HttpContext).Returns(context);
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.AddLogging()
.AddSingleton(contextAccessor.Object);
services.AddIdentity<TestUser, TestRole>();
services.AddSingleton<IUserStore<TestUser>, InMemoryStore<TestUser, TestRole>>();
services.AddSingleton<IRoleStore<TestRole>, InMemoryStore<TestUser, TestRole>>();
var app = new ApplicationBuilder(services.BuildServiceProvider());
app.UseCookieAuthentication();
// Act
var user = new TestUser
@ -61,7 +56,6 @@ namespace Microsoft.AspNetCore.Identity.InMemory.Test
// Assert
Assert.True(result.Succeeded);
context.VerifyAll();
auth.VerifyAll();
contextAccessor.VerifyAll();
}
@ -83,21 +77,20 @@ namespace Microsoft.AspNetCore.Identity.InMemory.Test
}
};
var auth = new Mock<AuthenticationManager>();
auth.Setup(a => a.AuthenticateAsync(It.IsAny<AuthenticateContext>())).Returns(Task.FromResult(0));
var context = new Mock<HttpContext>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
auth.Setup(a => a.AuthenticateAsync(context, It.IsAny<string>())).Returns(Task.FromResult(AuthenticateResult.None()));
var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(context.Object);
var services = new ServiceCollection();
services.AddLogging();
services.AddSingleton(contextAccessor.Object);
contextAccessor.Setup(a => a.HttpContext).Returns(context);
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.AddLogging()
.AddSingleton(contextAccessor.Object);
services.AddIdentity<TestUser, TestRole>();
services.AddSingleton<IUserStore<TestUser>, InMemoryStore<TestUser, TestRole>>();
services.AddSingleton<IRoleStore<TestRole>, InMemoryStore<TestUser, TestRole>>();
var app = new ApplicationBuilder(services.BuildServiceProvider());
app.UseCookieAuthentication();
// Act
var user = new TestUser
@ -113,5 +106,12 @@ namespace Microsoft.AspNetCore.Identity.InMemory.Test
Assert.Equal("refresh", await userManager.GetAuthenticationTokenAsync(user, authScheme, "refresh_token"));
Assert.Equal("access", await userManager.GetAuthenticationTokenAsync(user, authScheme, "access_token"));
}
private Mock<IAuthenticationService> MockAuth(HttpContext context)
{
var auth = new Mock<IAuthenticationService>();
context.RequestServices = new ServiceCollection().AddSingleton(auth.Object).BuildServiceProvider();
return auth;
}
}
}

View File

@ -11,11 +11,11 @@ using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.AspNetCore.Identity.Test;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
@ -27,14 +27,6 @@ namespace Microsoft.AspNetCore.Identity.InMemory
{
const string TestPassword = "1qaz!QAZ";
[Fact]
public void UseIdentityThrowsWithoutAddIdentity()
{
var builder = new WebHostBuilder()
.Configure(app => app.UseIdentity());
Assert.Throws<InvalidOperationException>(() => new TestServer(builder));
}
[Fact]
public async Task CanChangePasswordOptions()
{
@ -57,12 +49,15 @@ namespace Microsoft.AspNetCore.Identity.InMemory
public async Task CanCreateMeLoginAndCookieStopsWorkingAfterExpiration()
{
var clock = new TestClock();
var server = CreateServer(services => services.Configure<IdentityOptions>(options =>
var server = CreateServer(services =>
{
options.Cookies.ApplicationCookie.SystemClock = clock;
options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.Cookies.ApplicationCookie.SlidingExpiration = false;
}));
services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(10);
options.SlidingExpiration = false;
});
services.AddSingleton<ISystemClock>(clock);
});
var transaction1 = await SendAsync(server, "http://example.com/createMe");
Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode);
@ -96,10 +91,7 @@ namespace Microsoft.AspNetCore.Identity.InMemory
public async Task CanCreateMeLoginAndSecurityStampExtendsExpiration(bool rememberMe)
{
var clock = new TestClock();
var server = CreateServer(services => services.Configure<IdentityOptions>(options =>
{
options.Cookies.ApplicationCookie.SystemClock = clock;
}));
var server = CreateServer(services => services.AddSingleton<ISystemClock>(clock));
var transaction1 = await SendAsync(server, "http://example.com/createMe");
Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode);
@ -143,17 +135,20 @@ namespace Microsoft.AspNetCore.Identity.InMemory
public async Task CanAccessOldPrincipalDuringSecurityStampReplacement()
{
var clock = new TestClock();
var server = CreateServer(services => services.Configure<IdentityOptions>(options =>
var server = CreateServer(services =>
{
options.Cookies.ApplicationCookie.SystemClock = clock;
options.OnSecurityStampRefreshingPrincipal = c =>
services.Configure<IdentityOptions>(options =>
{
var newId = new ClaimsIdentity();
newId.AddClaim(new Claim("PreviousName", c.CurrentPrincipal.Identity.Name));
c.NewPrincipal.AddIdentity(newId);
return Task.FromResult(0);
};
}));
options.OnSecurityStampRefreshingPrincipal = c =>
{
var newId = new ClaimsIdentity();
newId.AddClaim(new Claim("PreviousName", c.CurrentPrincipal.Identity.Name));
c.NewPrincipal.AddIdentity(newId);
return Task.FromResult(0);
};
});
services.AddSingleton<ISystemClock>(clock);
});
var transaction1 = await SendAsync(server, "http://example.com/createMe");
Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode);
@ -233,7 +228,7 @@ namespace Microsoft.AspNetCore.Identity.InMemory
var builder = new WebHostBuilder()
.Configure(app =>
{
app.UseIdentity();
app.UseAuthentication();
app.Use(async (context, next) =>
{
var req = context.Request;
@ -282,14 +277,11 @@ namespace Microsoft.AspNetCore.Identity.InMemory
}
else if (req.Path == new PathString("/me"))
{
var auth = new AuthenticateContext("Application");
auth.Authenticated(context.User, new AuthenticationProperties().Items, new AuthenticationDescription().Items);
Describe(res, auth);
Describe(res, AuthenticateResult.Success(new AuthenticationTicket(context.User, null, "Application")));
}
else if (req.Path.StartsWithSegments(new PathString("/me"), out remainder))
{
var auth = new AuthenticateContext(remainder.Value.Substring(1));
await context.Authentication.AuthenticateAsync(auth);
var auth = await context.AuthenticateAsync(remainder.Value.Substring(1));
Describe(res, auth);
}
else if (req.Path == new PathString("/testpath") && testpath != null)
@ -307,17 +299,14 @@ namespace Microsoft.AspNetCore.Identity.InMemory
services.AddIdentity<TestUser, TestRole>();
services.AddSingleton<IUserStore<TestUser>, InMemoryStore<TestUser, TestRole>>();
services.AddSingleton<IRoleStore<TestRole>, InMemoryStore<TestUser, TestRole>>();
if (configureServices != null)
{
configureServices(services);
}
configureServices?.Invoke(services);
});
var server = new TestServer(builder);
server.BaseAddress = baseAddress;
return server;
}
private static void Describe(HttpResponse res, AuthenticateContext result)
private static void Describe(HttpResponse res, AuthenticateResult result)
{
res.StatusCode = 200;
res.ContentType = "text/xml";
@ -328,7 +317,7 @@ namespace Microsoft.AspNetCore.Identity.InMemory
}
if (result != null && result.Properties != null)
{
xml.Add(result.Properties.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value))));
xml.Add(result.Properties.Items.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value))));
}
using (var memory = new MemoryStream())
{

View File

@ -19,8 +19,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.Cookies" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(AspNetCoreVersion)" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(AspNetCoreVersion)" />

View File

@ -4,13 +4,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
@ -22,7 +21,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideUserStore()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddUserStore<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IUserStore<TestUser>>() as MyUberThingy;
Assert.NotNull(thingy);
@ -31,7 +31,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideRoleStore()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddRoleStore<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleStore<TestRole>>() as MyUberThingy;
Assert.NotNull(thingy);
@ -40,7 +41,9 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverridePrincipalFactory()
{
var services = new ServiceCollection().AddLogging();
var services = new ServiceCollection()
.AddLogging()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser, TestRole>()
.AddClaimsPrincipalFactory<MyClaimsPrincipalFactory>()
.AddUserManager<MyUserManager>()
@ -53,7 +56,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideRoleValidator()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddRoleValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IRoleValidator<TestRole>>() as MyUberThingy;
Assert.NotNull(thingy);
@ -62,7 +66,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideUserValidator()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddUserValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IUserValidator<TestUser>>() as MyUberThingy;
Assert.NotNull(thingy);
@ -71,7 +76,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverridePasswordValidator()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddPasswordValidator<MyUberThingy>();
var thingy = services.BuildServiceProvider().GetRequiredService<IPasswordValidator<TestUser>>() as MyUberThingy;
Assert.NotNull(thingy);
@ -80,7 +86,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideUserManager()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser, TestRole>()
.AddUserStore<NoopUserStore>()
.AddUserManager<MyUserManager>();
@ -91,7 +98,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideRoleManager()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser, TestRole>()
.AddRoleStore<NoopRoleStore>()
.AddRoleManager<MyRoleManager>();
@ -102,10 +110,11 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanOverrideSignInManager()
{
var services = new ServiceCollection();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>()
.AddLogging()
.AddIdentity<TestUser, TestRole>()
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>()
.AddLogging();
services.AddIdentity<TestUser, TestRole>()
.AddUserStore<NoopUserStore>()
.AddRoleStore<NoopRoleStore>()
.AddUserManager<MyUserManager>()
@ -118,7 +127,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void EnsureDefaultServices()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>();
var provider = services.BuildServiceProvider();
@ -135,7 +145,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void EnsureDefaultTokenProviders()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>().AddDefaultTokenProviders();
var provider = services.BuildServiceProvider();
@ -146,7 +157,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void AddManagerWithWrongTypesThrows()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
var builder = services.AddIdentity<TestUser, TestRole>();
Assert.Throws<InvalidOperationException>(() => builder.AddUserManager<UserManager<TestUser>>());
Assert.Throws<InvalidOperationException>(() => builder.AddRoleManager<RoleManager<TestRole>>());
@ -159,7 +171,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void AddTokenProviderWithWrongTypesThrows()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
var builder = services.AddIdentity<TestUser, TestRole>();
Assert.Throws<InvalidOperationException>(() => builder.AddTokenProvider<object>("whatevs"));
Assert.Throws<InvalidOperationException>(() => builder.AddTokenProvider("whatevs", typeof(object)));

View File

@ -36,11 +36,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(ClaimTypes.Name, options.ClaimsIdentity.UserNameClaimType);
Assert.Equal(ClaimTypes.NameIdentifier, options.ClaimsIdentity.UserIdClaimType);
Assert.Equal("AspNet.Identity.SecurityStamp", options.ClaimsIdentity.SecurityStampClaimType);
Assert.True(options.Cookies.ApplicationCookie.AutomaticAuthenticate);
Assert.False(options.Cookies.ExternalCookie.AutomaticAuthenticate);
Assert.False(options.Cookies.TwoFactorRememberMeCookie.AutomaticAuthenticate);
Assert.False(options.Cookies.TwoFactorUserIdCookie.AutomaticAuthenticate);
}
[Fact]
@ -73,8 +68,8 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(roleClaimType, config["identity:claimsidentity:roleclaimtype"]);
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(config);
services.AddIdentity<TestUser,TestRole>();
services.Configure<IdentityOptions>(config.GetSection("identity"));
var accessor = services.BuildServiceProvider().GetRequiredService<IOptions<IdentityOptions>>();
Assert.NotNull(accessor);
var options = accessor.Value;
@ -106,7 +101,7 @@ namespace Microsoft.AspNetCore.Identity.Test
builder.AddInMemoryCollection(dic);
var config = builder.Build();
var services = new ServiceCollection();
services.Configure<IdentityOptions>(config.GetSection("identity"));
services.AddSingleton<IConfiguration>(config);
services.AddIdentity<TestUser, TestRole>(o => { o.User.RequireUniqueEmail = false; o.Lockout.MaxFailedAccessAttempts++; });
var accessor = services.BuildServiceProvider().GetRequiredService<IOptions<IdentityOptions>>();
Assert.NotNull(accessor);
@ -119,6 +114,7 @@ namespace Microsoft.AspNetCore.Identity.Test
public void CanCustomizeIdentityOptions()
{
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build())
.Configure<IdentityOptions>(options => options.Password.RequiredLength = -1);
services.AddIdentity<TestUser,TestRole>();
var serviceProvider = services.BuildServiceProvider();
@ -139,7 +135,8 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void CanSetupIdentityOptions()
{
var services = new ServiceCollection();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build());
services.AddIdentity<TestUser,TestRole>(options => options.User.RequireUniqueEmail = true);
var serviceProvider = services.BuildServiceProvider();

View File

@ -8,7 +8,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
@ -18,15 +17,47 @@ namespace Microsoft.AspNetCore.Identity.Test
{
public class SecurityStampTest
{
private class NoopHandler : IAuthenticationHandler
{
public Task<AuthenticateResult> AuthenticateAsync()
{
throw new NotImplementedException();
}
public Task ChallengeAsync(ChallengeContext context)
{
throw new NotImplementedException();
}
public Task<bool> HandleRequestAsync()
{
throw new NotImplementedException();
}
public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context)
{
throw new NotImplementedException();
}
public Task SignInAsync(SignInContext context)
{
throw new NotImplementedException();
}
public Task SignOutAsync(SignOutContext context)
{
throw new NotImplementedException();
}
}
[Fact]
public async Task OnValidatePrincipalThrowsWithEmptyServiceCollection()
{
var scheme = new IdentityOptions().Cookies.ApplicationCookieAuthenticationScheme;
var httpContext = new Mock<HttpContext>();
httpContext.Setup(c => c.RequestServices).Returns(new ServiceCollection().BuildServiceProvider());
var id = new ClaimsPrincipal(new ClaimsIdentity(scheme));
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, scheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
var id = new ClaimsPrincipal(new ClaimsIdentity(IdentityCookieOptions.ApplicationScheme));
var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, IdentityCookieOptions.ApplicationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(IdentityCookieOptions.ApplicationScheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions());
var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => SecurityStampValidator.ValidatePrincipalAsync(context));
}
@ -56,13 +87,13 @@ namespace Microsoft.AspNetCore.Identity.Test
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object));
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
var ticket = new AuthenticationTicket(principal,
properties,
identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(identityOptions.Cookies.ApplicationCookieAuthenticationScheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal);
@ -90,7 +121,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object));
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
var id = new ClaimsIdentity(identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
@ -98,7 +129,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow.AddSeconds(-1) },
identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(identityOptions.Cookies.ApplicationCookieAuthenticationScheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal);
@ -125,7 +156,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object));
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
var id = new ClaimsIdentity(identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
@ -133,7 +164,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties(),
identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(identityOptions.Cookies.ApplicationCookieAuthenticationScheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal);
@ -161,7 +192,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var services = new ServiceCollection();
services.AddSingleton(options.Object);
services.AddSingleton(signInManager.Object);
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object));
services.AddSingleton<ISecurityStampValidator>(new SecurityStampValidator<TestUser>(options.Object, signInManager.Object, new SystemClock()));
httpContext.Setup(c => c.RequestServices).Returns(services.BuildServiceProvider());
var id = new ClaimsIdentity(identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
@ -169,7 +200,7 @@ namespace Microsoft.AspNetCore.Identity.Test
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow },
identityOptions.Cookies.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(identityOptions.Cookies.ApplicationCookieAuthenticationScheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties);
Assert.NotNull(context.Options);
Assert.NotNull(context.Principal);

View File

@ -7,10 +7,10 @@ using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Moq;
using Xunit;
@ -203,11 +203,10 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth, user.Id, isPersistent);
var helper = SetupSignInManager(manager.Object, context.Object);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
SetupSignIn(context, auth, user.Id, isPersistent);
var helper = SetupSignInManager(manager.Object, context);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
@ -215,7 +214,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -229,11 +227,10 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth, user.Id, false);
var helper = SetupSignInManager(manager.Object, context.Object);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
SetupSignIn(context, auth, user.Id, false);
var helper = SetupSignInManager(manager.Object, context);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
@ -241,7 +238,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -257,11 +253,10 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
SetupSignIn(auth);
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var helper = SetupSignInManager(manager.Object, context.Object);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
SetupSignIn(context, auth);
var helper = SetupSignInManager(manager.Object, context);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
@ -269,7 +264,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -296,12 +290,12 @@ namespace Microsoft.AspNetCore.Identity.Test
{
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
}
var context = new Mock<HttpContext>();
var helper = SetupSignInManager(manager.Object, context.Object);
var auth = new Mock<AuthenticationManager>();
auth.Setup(a => a.SignInAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id))).Returns(Task.FromResult(0)).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var context = new DefaultHttpContext();
var helper = SetupSignInManager(manager.Object, context);
var auth = MockAuth(context);
auth.Setup(a => a.SignInAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false);
@ -310,7 +304,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.False(result.Succeeded);
Assert.True(result.RequiresTwoFactor);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -334,19 +327,19 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable();
}
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var helper = SetupSignInManager(manager.Object, context.Object);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
if (bypass)
{
SetupSignIn(auth, user.Id, false, loginProvider);
SetupSignIn(context, auth, user.Id, false, loginProvider);
}
else
{
auth.Setup(a => a.SignInAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id))).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignInAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(id => id.FindFirstValue(ClaimTypes.Name) == user.Id),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
}
// Act
@ -356,7 +349,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(bypass, result.Succeeded);
Assert.Equal(!bypass, result.RequiresTwoFactor);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -384,21 +376,21 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, providerName ?? TokenOptions.DefaultAuthenticatorProvider, code)).ReturnsAsync(true).Verifiable();
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
var helper = SetupSignInManager(manager.Object, context.Object);
if (providerName != null)
{
helper.Options.Tokens.AuthenticatorTokenProvider = providerName;
}
var id = helper.StoreTwoFactorInfo(user.Id, null);
SetupSignIn(auth, user.Id, isPersistent);
auth.Setup(a => a.AuthenticateAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).ReturnsAsync(id).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(context, auth, user.Id, isPersistent);
auth.Setup(a => a.AuthenticateAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))).Verifiable();
if (rememberClient)
{
auth.Setup(a => a.SignInAsync(
auth.Setup(a => a.SignInAsync(context,
helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
&& i.Identities.First().AuthenticationType == helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme),
@ -411,7 +403,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -432,28 +423,28 @@ namespace Microsoft.AspNetCore.Identity.Test
{
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
}
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
var loginProvider = "loginprovider";
var helper = SetupSignInManager(manager.Object, context.Object);
var id = helper.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
if (externalLogin)
{
auth.Setup(a => a.SignInAsync(
auth.Setup(a => a.SignInAsync(context,
helper.Options.Cookies.ApplicationCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider
&& i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(helper.Options.Cookies.ExternalCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.ExternalCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
}
else
{
SetupSignIn(auth, user.Id);
SetupSignIn(context, auth, user.Id);
}
auth.Setup(a => a.AuthenticateAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).ReturnsAsync(id).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
auth.Setup(a => a.AuthenticateAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))).Verifiable();
// Act
var result = await helper.TwoFactorRecoveryCodeSignInAsync(bypassCode);
@ -461,7 +452,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -484,11 +474,10 @@ namespace Microsoft.AspNetCore.Identity.Test
}
manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth, user.Id, isPersistent, loginProvider);
var helper = SetupSignInManager(manager.Object, context.Object);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
SetupSignIn(context, auth, user.Id, isPersistent, loginProvider);
// Act
var result = await helper.ExternalLoginSignInAsync(loginProvider, providerKey, isPersistent);
@ -496,7 +485,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -509,9 +497,8 @@ namespace Microsoft.AspNetCore.Identity.Test
{
// Setup
var user = new TestUser { UserName = "Foo" };
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var loginProvider = "loginprovider";
var id = new ClaimsIdentity();
if (externalLogin)
@ -520,24 +507,24 @@ namespace Microsoft.AspNetCore.Identity.Test
}
// REVIEW: auth changes we lost the ability to mock is persistent
//var properties = new AuthenticationProperties { IsPersistent = isPersistent };
auth.Setup(a => a.AuthenticateAsync(It.Is<AuthenticateContext>(c => c.AuthenticationScheme == new IdentityCookieOptions().ApplicationCookieAuthenticationScheme)))
.Returns(Task.FromResult(0)).Verifiable();
var authResult = AuthenticateResult.None();
auth.Setup(a => a.AuthenticateAsync(context, new IdentityCookieOptions().ApplicationCookieAuthenticationScheme))
.Returns(Task.FromResult(authResult)).Verifiable();
var manager = SetupUserManager(user);
var signInManager = new Mock<SignInManager<TestUser>>(manager.Object,
new HttpContextAccessor { HttpContext = context.Object },
new HttpContextAccessor { HttpContext = context },
new Mock<IUserClaimsPrincipalFactory<TestUser>>().Object,
null, null)
{ CallBase = true };
//signInManager.Setup(s => s.SignInAsync(user, It.Is<AuthenticationProperties>(p => p.IsPersistent == isPersistent),
//externalLogin? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable();
signInManager.Setup(s => s.SignInAsync(user, It.IsAny<AuthenticationProperties>(), null)).Returns(Task.FromResult(0)).Verifiable();
signInManager.Object.Context = context.Object;
signInManager.Object.Context = context;
// Act
await signInManager.Object.RefreshSignInAsync(user);
// Assert
context.Verify();
auth.Verify();
signInManager.Verify();
}
@ -573,39 +560,39 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable();
}
manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code)).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
var twoFactorInfo = new SignInManager<TestUser>.TwoFactorAuthenticationInfo { UserId = user.Id };
var loginProvider = "loginprovider";
var helper = SetupSignInManager(manager.Object, context.Object);
var id = helper.StoreTwoFactorInfo(user.Id, externalLogin ? loginProvider : null);
if (externalLogin)
{
auth.Setup(a => a.SignInAsync(
auth.Setup(a => a.SignInAsync(context,
helper.Options.Cookies.ApplicationCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider
&& i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
// REVIEW: restore ability to test is persistent
//It.Is<AuthenticationProperties>(v => v.IsPersistent == isPersistent))).Verifiable();
auth.Setup(a => a.SignOutAsync(helper.Options.Cookies.ExternalCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.ExternalCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
}
else
{
SetupSignIn(auth, user.Id);
SetupSignIn(context, auth, user.Id);
}
if (rememberClient)
{
auth.Setup(a => a.SignInAsync(
auth.Setup(a => a.SignInAsync(context,
helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
&& i.Identities.First().AuthenticationType == helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme),
It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
//It.Is<AuthenticationProperties>(v => v.IsPersistent == true))).Returns(Task.FromResult(0)).Verifiable();
}
auth.Setup(a => a.AuthenticateAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).ReturnsAsync(id).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
auth.Setup(a => a.AuthenticateAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(id, null, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme))).Verifiable();
// Act
var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent, rememberClient);
@ -613,7 +600,6 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
@ -623,23 +609,22 @@ namespace Microsoft.AspNetCore.Identity.Test
// Setup
var user = new TestUser { UserName = "Foo" };
var manager = SetupUserManager(user);
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var helper = SetupSignInManager(manager.Object, context);
auth.Setup(a => a.SignInAsync(
context,
manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(i => i.FindFirstValue(ClaimTypes.Name) == user.Id
&& i.Identities.First().AuthenticationType == manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme),
It.Is<AuthenticationProperties>(v => v.IsPersistent == true))).Returns(Task.FromResult(0)).Verifiable();
var helper = SetupSignInManager(manager.Object, context.Object);
// Act
await helper.RememberTwoFactorClientAsync(user);
// Assert
manager.Verify();
context.Verify();
auth.Verify();
}
@ -659,14 +644,14 @@ namespace Microsoft.AspNetCore.Identity.Test
manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable();
manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable();
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth);
var context = new DefaultHttpContext();
var auth = MockAuth(context);
SetupSignIn(context, auth);
var id = new ClaimsIdentity(manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme);
id.AddClaim(new Claim(ClaimTypes.Name, user.Id));
auth.Setup(a => a.AuthenticateAsync(manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme)).ReturnsAsync(new ClaimsPrincipal(id)).Verifiable();
var helper = SetupSignInManager(manager.Object, context.Object);
auth.Setup(a => a.AuthenticateAsync(context, manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme))
.ReturnsAsync(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(id), null, manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme))).Verifiable();
var helper = SetupSignInManager(manager.Object, context);
// Act
var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false);
@ -674,10 +659,16 @@ namespace Microsoft.AspNetCore.Identity.Test
// Assert
Assert.True(result.Succeeded);
manager.Verify();
context.Verify();
auth.Verify();
}
private Mock<IAuthenticationService> MockAuth(HttpContext context)
{
var auth = new Mock<IAuthenticationService>();
context.RequestServices = new ServiceCollection().AddSingleton(auth.Object).BuildServiceProvider();
return auth;
}
[Theory]
[InlineData("Microsoft.AspNetCore.Identity.Authentication.Application")]
[InlineData("Foo")]
@ -685,20 +676,18 @@ namespace Microsoft.AspNetCore.Identity.Test
{
// Setup
var manager = MockHelpers.TestUserManager<TestUser>();
manager.Options.Cookies.ApplicationCookie.AuthenticationScheme = authenticationScheme;
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
auth.Setup(a => a.SignOutAsync(authenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(manager.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(manager.Options.Cookies.ExternalCookieAuthenticationScheme)).Returns(Task.FromResult(0)).Verifiable();
var helper = SetupSignInManager(manager, context.Object, null, manager.Options);
manager.Options.Cookies.ApplicationCookieAuthenticationScheme = authenticationScheme;
var context = new DefaultHttpContext();
var auth = MockAuth(context);
auth.Setup(a => a.SignOutAsync(context, authenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, manager.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
auth.Setup(a => a.SignOutAsync(context, manager.Options.Cookies.ExternalCookieAuthenticationScheme, It.IsAny<AuthenticationProperties>())).Returns(Task.FromResult(0)).Verifiable();
var helper = SetupSignInManager(manager, context, null, manager.Options);
// Act
await helper.SignOutAsync();
// Assert
context.Verify();
auth.Verify();
}
@ -811,18 +800,17 @@ namespace Microsoft.AspNetCore.Identity.Test
{
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
}
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
if (confirmed)
{
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth);
SetupSignIn(context, auth);
}
var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedEmail = true;
var logStore = new StringBuilder();
var helper = SetupSignInManager(manager.Object, context.Object, logStore, identityOptions);
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions);
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
@ -834,13 +822,13 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed email."));
manager.Verify();
context.Verify();
auth.Verify();
}
private static void SetupSignIn(Mock<AuthenticationManager> auth, string userId = null, bool? isPersistent = null, string loginProvider = null)
private static void SetupSignIn(HttpContext context, Mock<IAuthenticationService> auth, string userId = null, bool? isPersistent = null, string loginProvider = null)
{
auth.Setup(a => a.SignInAsync(new IdentityCookieOptions().ApplicationCookieAuthenticationScheme,
auth.Setup(a => a.SignInAsync(context,
new IdentityCookieOptions().ApplicationCookieAuthenticationScheme,
It.Is<ClaimsPrincipal>(id =>
(userId == null || id.FindFirstValue(ClaimTypes.NameIdentifier) == userId) &&
(loginProvider == null || id.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider)),
@ -856,19 +844,18 @@ namespace Microsoft.AspNetCore.Identity.Test
var user = new TestUser { UserName = "Foo" };
var manager = SetupUserManager(user);
manager.Setup(m => m.IsPhoneNumberConfirmedAsync(user)).ReturnsAsync(confirmed).Verifiable();
var context = new Mock<HttpContext>();
var auth = new Mock<AuthenticationManager>();
var context = new DefaultHttpContext();
var auth = MockAuth(context);
if (confirmed)
{
manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable();
context.Setup(c => c.Authentication).Returns(auth.Object).Verifiable();
SetupSignIn(auth);
SetupSignIn(context, auth);
}
var identityOptions = new IdentityOptions();
identityOptions.SignIn.RequireConfirmedPhoneNumber = true;
var logStore = new StringBuilder();
var helper = SetupSignInManager(manager.Object, context.Object, logStore, identityOptions);
var helper = SetupSignInManager(manager.Object, context, logStore, identityOptions);
// Act
var result = await helper.PasswordSignInAsync(user, "password", false, false);
@ -878,7 +865,6 @@ namespace Microsoft.AspNetCore.Identity.Test
Assert.NotEqual(confirmed, result.IsNotAllowed);
Assert.Equal(confirmed, !logStore.ToString().Contains($"User {user.Id} cannot sign in without a confirmed phone number."));
manager.Verify();
context.Verify();
auth.Verify();
}
}

View File

@ -10,6 +10,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Moq;
using Xunit;
@ -21,7 +22,9 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void EnsureDefaultServicesDefaultsWithStoreWorks()
{
var config = new ConfigurationBuilder().Build();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddTransient<IUserStore<TestUser>, NoopUserStore>();
services.AddIdentity<TestUser, TestRole>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -35,7 +38,9 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void AddUserManagerWithCustomManagerReturnsSameInstance()
{
var config = new ConfigurationBuilder().Build();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddTransient<IUserStore<TestUser>, NoopUserStore>()
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@ -682,10 +687,13 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void UserManagerWillUseTokenProviderInstance()
{
var services = new ServiceCollection();
var provider = new ATokenProvider();
services.AddLogging()
.AddIdentity<TestUser, TestRole>(o => o.Tokens.ProviderMap.Add("A", new TokenProviderDescriptor(typeof(ATokenProvider))
var config = new ConfigurationBuilder().Build();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddLogging();
services.AddIdentity<TestUser, TestRole>(o => o.Tokens.ProviderMap.Add("A", new TokenProviderDescriptor(typeof(ATokenProvider))
{
ProviderInstance = provider
})).AddUserStore<NoopUserStore>();
@ -836,10 +844,13 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public void UserManagerWillUseTokenProviderInstanceOverDefaults()
{
var services = new ServiceCollection();
var provider = new ATokenProvider();
services.AddLogging()
.AddIdentity<TestUser, TestRole>(o => o.Tokens.ProviderMap.Add(TokenOptions.DefaultProvider, new TokenProviderDescriptor(typeof(ATokenProvider))
var config = new ConfigurationBuilder().Build();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddLogging();
services.AddIdentity<TestUser, TestRole>(o => o.Tokens.ProviderMap.Add(TokenOptions.DefaultProvider, new TokenProviderDescriptor(typeof(ATokenProvider))
{
ProviderInstance = provider
})).AddUserStore<NoopUserStore>().AddDefaultTokenProviders();
@ -1668,14 +1679,17 @@ namespace Microsoft.AspNetCore.Identity.Test
[Fact]
public async Task CanCustomizeUserValidatorErrors()
{
var services = new ServiceCollection();
var store = new Mock<IUserEmailStore<TestUser>>();
var describer = new TestErrorDescriber();
services.AddSingleton<IdentityErrorDescriber>(describer)
.AddSingleton<IUserStore<TestUser>>(store.Object)
.AddIdentity<TestUser, TestRole>();
services.AddLogging();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
var config = new ConfigurationBuilder().Build();
var services = new ServiceCollection()
.AddSingleton<IConfiguration>(config)
.AddLogging()
.AddSingleton<IdentityErrorDescriber>(describer)
.AddSingleton<IUserStore<TestUser>>(store.Object)
.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddIdentity<TestUser, TestRole>();
var manager = services.BuildServiceProvider().GetRequiredService<UserManager<TestUser>>();