diff --git a/Identity.sln b/Identity.sln index 45e7de652..a3fce5e10 100644 --- a/Identity.sln +++ b/Identity.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26123.0 +VisualStudioVersion = 15.0.26228.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}" EndProject @@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNet.Identity.A EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Identity.Specification.Tests", "src\Microsoft.AspNetCore.Identity.Specification.Tests\Microsoft.AspNetCore.Identity.Specification.Tests.csproj", "{5608E828-DD54-4E2A-B73C-FC22268BE797}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.Cookies", "..\Security\src\Microsoft.AspNetCore.Authentication.Cookies\Microsoft.AspNetCore.Authentication.Cookies.csproj", "{D747780A-7C44-4714-AD6C-3B226A7EB2FB}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -130,6 +132,18 @@ Global {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|Mixed Platforms.Build.0 = Release|Any CPU {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.ActiveCfg = Release|Any CPU {5608E828-DD54-4E2A-B73C-FC22268BE797}.Release|x86.Build.0 = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|x86.ActiveCfg = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Debug|x86.Build.0 = Debug|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|Any CPU.Build.0 = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|x86.ActiveCfg = Release|Any CPU + {D747780A-7C44-4714-AD6C-3B226A7EB2FB}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -144,5 +158,6 @@ Global {4490894C-3572-4E63-86F1-EE5105CE8A06} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {6A74C6EA-B241-4D6B-BCE4-BF89EC1D2475} = {0F647068-6602-4E24-B1DC-8ED91481A50A} {5608E828-DD54-4E2A-B73C-FC22268BE797} = {0F647068-6602-4E24-B1DC-8ED91481A50A} + {D747780A-7C44-4714-AD6C-3B226A7EB2FB} = {0F647068-6602-4E24-B1DC-8ED91481A50A} EndGlobalSection EndGlobal diff --git a/samples/IdentitySample.Mvc/Controllers/ManageController.cs b/samples/IdentitySample.Mvc/Controllers/ManageController.cs index 666b97f3b..70785cdc7 100644 --- a/samples/IdentitySample.Mvc/Controllers/ManageController.cs +++ b/samples/IdentitySample.Mvc/Controllers/ManageController.cs @@ -10,6 +10,7 @@ using IdentitySample.Models; using IdentitySample.Models.ManageViewModels; using IdentitySample.Services; +using Microsoft.AspNetCore.Authentication; namespace IdentitySamples.Controllers { @@ -19,6 +20,7 @@ public class ManageController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager; + private readonly IAuthenticationSchemeProvider _schemes; private readonly IEmailSender _emailSender; private readonly ISmsSender _smsSender; private readonly ILogger _logger; @@ -26,12 +28,14 @@ public class ManageController : Controller public ManageController( UserManager userManager, SignInManager signInManager, + IAuthenticationSchemeProvider schemes, IEmailSender emailSender, ISmsSender smsSender, ILoggerFactory loggerFactory) { _userManager = userManager; _signInManager = signInManager; + _schemes = schemes; _emailSender = emailSender; _smsSender = smsSender; _logger = loggerFactory.CreateLogger(); @@ -308,7 +312,8 @@ public async Task ManageLogins(ManageMessageId? message = null) 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 { diff --git a/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs b/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs index 70948effa..b7bd94a19 100644 --- a/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs +++ b/samples/IdentitySample.Mvc/Models/ManageViewModels/ManageLoginsViewModel.cs @@ -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 @@ public class ManageLoginsViewModel { public IList CurrentLogins { get; set; } - public IList OtherLogins { get; set; } + public IList OtherLogins { get; set; } } } diff --git a/samples/IdentitySample.Mvc/Startup.cs b/samples/IdentitySample.Mvc/Startup.cs index f61d83ac2..4a8d6338d 100644 --- a/samples/IdentitySample.Mvc/Startup.cs +++ b/samples/IdentitySample.Mvc/Startup.cs @@ -68,7 +68,6 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerF app.UseStaticFiles(); - app.UseIdentity(); // To configure external authentication please see http://go.microsoft.com/fwlink/?LinkID=532715 app.UseMvc(routes => diff --git a/samples/IdentitySample.Mvc/Views/Account/Login.cshtml b/samples/IdentitySample.Mvc/Views/Account/Login.cshtml index e788eec18..8127b0267 100644 --- a/samples/IdentitySample.Mvc/Views/Account/Login.cshtml +++ b/samples/IdentitySample.Mvc/Views/Account/Login.cshtml @@ -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 SignInManager +@inject IAuthenticationSchemeProvider SchemeProvider @{ ViewData["Title"] = "Log in"; @@ -59,7 +60,8 @@

Use another service to log in.


@{ - var loginProviders = SignInManager.GetExternalAuthenticationSchemes().ToList(); + var schemes = await SchemeProvider.GetAllSchemesAsync(); + var loginProviders = schemes.ToList(); if (loginProviders.Count == 0) {
@@ -76,7 +78,7 @@

@foreach (var provider in loginProviders) { - + }

diff --git a/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml b/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml index 1bfb1d8af..095e18653 100644 --- a/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml +++ b/samples/IdentitySample.Mvc/Views/Manage/ManageLogins.cshtml @@ -46,7 +46,7 @@

@foreach (var provider in Model.OtherLogins) { - + }

diff --git a/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs index bf5439411..c6c132dbb 100644 --- a/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs +++ b/src/Microsoft.AspNetCore.Identity.Specification.Tests/IdentitySpecificationTestBase.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -60,6 +61,7 @@ protected virtual bool ShouldSkipDbTests() /// protected virtual void SetupIdentityServices(IServiceCollection services, object context = null) { + services.AddSingleton(new ConfigurationBuilder().Build()); services.AddSingleton(); services.AddIdentity(options => { diff --git a/src/Microsoft.AspNetCore.Identity/BuilderExtensions.cs b/src/Microsoft.AspNetCore.Identity/BuilderExtensions.cs index b12c2c507..e5136c7be 100644 --- a/src/Microsoft.AspNetCore.Identity/BuilderExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity/BuilderExtensions.cs @@ -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 { /// @@ -19,25 +15,7 @@ public static class BuilderExtensions /// /// The instance this method extends. /// The instance this method extends. - public static IApplicationBuilder UseIdentity(this IApplicationBuilder app) - { - if (app == null) - { - throw new ArgumentNullException(nameof(app)); - } - - var marker = app.ApplicationServices.GetService(); - if (marker == null) - { - throw new InvalidOperationException(Resources.MustCallAddIdentity); - } - - var options = app.ApplicationServices.GetRequiredService>().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; } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity/IdentityCookieOptions.cs b/src/Microsoft.AspNetCore.Identity/IdentityCookieOptions.cs index 4d694c700..5e9afd81e 100644 --- a/src/Microsoft.AspNetCore.Identity/IdentityCookieOptions.cs +++ b/src/Microsoft.AspNetCore.Identity/IdentityCookieOptions.cs @@ -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"; - /// - /// Constructs a new instance of . + /// The scheme used to identify application authentication cookies. /// - 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 ApplicationScheme = CookiePrefix + ".Application"; - ExternalCookie = new CookieAuthenticationOptions - { - AutomaticAuthenticate = false, - AuthenticationScheme = DefaultExternalScheme, - CookieName = DefaultExternalScheme, - ExpireTimeSpan = TimeSpan.FromMinutes(5) - }; + /// + /// The scheme used to identify external authentication cookies. + /// + public static readonly string ExternalScheme = CookiePrefix + ".External"; - TwoFactorRememberMeCookie = new CookieAuthenticationOptions - { - AutomaticAuthenticate = false, - AuthenticationScheme = DefaultTwoFactorRememberMeScheme, - CookieName = DefaultTwoFactorRememberMeScheme - }; + /// + /// The scheme used to identify Two Factor authentication cookies for saving the Remember Me state. + /// + public static readonly string TwoFactorRememberMeScheme = CookiePrefix + ".TwoFactorRememberMe"; - TwoFactorUserIdCookie = new CookieAuthenticationOptions - { - AutomaticAuthenticate = false, - AuthenticationScheme = DefaultTwoFactorUserIdScheme, - CookieName = DefaultTwoFactorUserIdScheme, - ExpireTimeSpan = TimeSpan.FromMinutes(5) - }; - } + /// + /// The scheme used to identify Two Factor authentication cookies for round tripping user identities. + /// + public static readonly string TwoFactorUserIdScheme = CookiePrefix + ".TwoFactorUserId"; /// /// The options for the application cookie. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)] public CookieAuthenticationOptions ApplicationCookie { get; set; } /// /// The options for the external cookie. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)] public CookieAuthenticationOptions ExternalCookie { get; set; } /// /// The options for the two factor remember me cookie. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)] public CookieAuthenticationOptions TwoFactorRememberMeCookie { get; set; } /// /// The options for the two factor user id cookie. /// + [Obsolete("See https://go.microsoft.com/fwlink/?linkid=845470", error: true)] public CookieAuthenticationOptions TwoFactorUserIdCookie { get; set; } /// /// Gets the scheme used to identify application authentication cookies. /// /// The scheme used to identify application authentication cookies. - public string ApplicationCookieAuthenticationScheme => ApplicationCookie?.AuthenticationScheme; + public string ApplicationCookieAuthenticationScheme { get; set; } = ApplicationScheme; /// /// Gets the scheme used to identify external authentication cookies. /// /// The scheme used to identify external authentication cookies. - public string ExternalCookieAuthenticationScheme => ExternalCookie?.AuthenticationScheme; + public string ExternalCookieAuthenticationScheme { get; set; } = ExternalScheme; /// /// Gets the scheme used to identify Two Factor authentication cookies for round tripping user identities. /// /// The scheme used to identify user identity 2fa authentication cookies. - public string TwoFactorUserIdCookieAuthenticationScheme => TwoFactorUserIdCookie?.AuthenticationScheme; + public string TwoFactorUserIdCookieAuthenticationScheme { get; set; } = TwoFactorUserIdScheme; /// /// Gets the scheme used to identify Two Factor authentication cookies for saving the Remember Me state. /// /// The scheme used to identify remember me application authentication cookies. - public string TwoFactorRememberMeCookieAuthenticationScheme => TwoFactorRememberMeCookie?.AuthenticationScheme; + public string TwoFactorRememberMeCookieAuthenticationScheme { get; set; } = TwoFactorRememberMeScheme; } } diff --git a/src/Microsoft.AspNetCore.Identity/IdentityMarkerService.cs b/src/Microsoft.AspNetCore.Identity/IdentityMarkerService.cs deleted file mode 100644 index bb28f7bf7..000000000 --- a/src/Microsoft.AspNetCore.Identity/IdentityMarkerService.cs +++ /dev/null @@ -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 -{ - /// - /// Used to verify AddIdentity was called on a ServiceCollection - /// - public class IdentityMarkerService { } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs index b8fc6126c..fc782ae49 100644 --- a/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Identity/IdentityServiceCollectionExtensions.cs @@ -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 /// public static class IdentityServiceCollectionExtensions { + internal class IdentityConfigureOptions : ConfigureOptions + { + public IdentityConfigureOptions(IConfiguration config) : + base(options => config.GetSection("Identity").Bind(options)) + { } + } + /// /// Adds the default identity system configuration for the specified User and Role types. /// @@ -29,6 +39,24 @@ public static IdentityBuilder AddIdentity( return services.AddIdentity(setupAction: null); } + /// + /// Configures the application cookie. + /// + /// The services available in the application. + /// An action to configure the . + /// The services. + public static IServiceCollection ConfigureApplicationCookie(this IServiceCollection services, Action configure) + => services.Configure(IdentityCookieOptions.ApplicationScheme, configure); + + /// + /// Configure the external cookie. + /// + /// The services available in the application. + /// An action to configure the . + /// The services. + public static IServiceCollection ConfigureExternalCookie(this IServiceCollection services, Action configure) + => services.Configure(IdentityCookieOptions.ExternalScheme, configure); + /// /// Adds and configures the identity system for the specified User and Role types. /// @@ -44,16 +72,40 @@ public static IdentityBuilder AddIdentity( where TRole : class { // Services used by identity - services.AddAuthentication(options => + services.AddAuthenticationCore(options => + { + 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 => { - // This is the Default value for ExternalCookieAuthenticationScheme - options.SignInScheme = new IdentityCookieOptions().ExternalCookieAuthenticationScheme; + o.CookieName = IdentityCookieOptions.TwoFactorUserIdScheme; + o.ExpireTimeSpan = TimeSpan.FromMinutes(5); }); // Hosting doesn't add IHttpContextAccessor by default services.TryAddSingleton(); // Identity services - services.TryAddSingleton(); services.TryAddScoped, UserValidator>(); services.TryAddScoped, PasswordValidator>(); services.TryAddScoped, PasswordHasher>(); @@ -67,6 +119,7 @@ public static IdentityBuilder AddIdentity( services.TryAddScoped, SignInManager>(); services.TryAddScoped, RoleManager>(); + services.AddSingleton, IdentityConfigureOptions>(); if (setupAction != null) { services.Configure(setupAction); diff --git a/src/Microsoft.AspNetCore.Identity/Microsoft.AspNetCore.Identity.csproj b/src/Microsoft.AspNetCore.Identity/Microsoft.AspNetCore.Identity.csproj index d42066b83..7eff230fb 100644 --- a/src/Microsoft.AspNetCore.Identity/Microsoft.AspNetCore.Identity.csproj +++ b/src/Microsoft.AspNetCore.Identity/Microsoft.AspNetCore.Identity.csproj @@ -10,9 +10,10 @@ - + + diff --git a/src/Microsoft.AspNetCore.Identity/SecurityStampValidator.cs b/src/Microsoft.AspNetCore.Identity/SecurityStampValidator.cs index 7b4136e48..e5873a69d 100644 --- a/src/Microsoft.AspNetCore.Identity/SecurityStampValidator.cs +++ b/src/Microsoft.AspNetCore.Identity/SecurityStampValidator.cs @@ -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 @@ public class SecurityStampValidator : ISecurityStampValidator where TUser { private readonly SignInManager _signInManager; private readonly IdentityOptions _options; + private ISystemClock _clock; /// /// Creates a new instance of . /// /// Used to access the . /// The . - public SecurityStampValidator(IOptions options, SignInManager signInManager) + /// The system clock. + public SecurityStampValidator(IOptions options, SignInManager signInManager, ISystemClock clock) { if (options == null) { @@ -36,6 +39,7 @@ public SecurityStampValidator(IOptions options, SignInManager @@ -48,9 +52,9 @@ public SecurityStampValidator(IOptions options, SignInManager CanSignInAsync(TUser user) /// The task object representing the asynchronous operation. 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); } /// @@ -192,7 +188,7 @@ public virtual async Task SignInAsync(TUser user, AuthenticationProperties authe { 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 @@ await Context.Authentication.SignInAsync(Options.Cookies.ApplicationCookieAuthen /// 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); } /// @@ -333,8 +329,8 @@ public virtual async Task CheckPasswordSignInAsync(TUser user, str public virtual async Task 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); } /// @@ -348,7 +344,7 @@ public virtual async Task RememberTwoFactorClientAsync(TUser user) 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 @@ await Context.Authentication.SignInAsync(Options.Cookies.TwoFactorRememberMeCook /// The task object representing the asynchronous operation. public virtual Task ForgetTwoFactorClientAsync() { - return Context.Authentication.SignOutAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme); + return Context.SignOutAsync(Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme); } /// @@ -399,10 +395,10 @@ private async Task DoTwoFactorSignInAsync(TUser user, TwoFactorAuthenticationInf // 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 @@ public virtual async Task ExternalLoginSignInAsync(string loginPro return await SignInOrTwoFactorAsync(user, isPersistent, loginProvider, bypassTwoFactor); } - /// - /// Gets a collection of s for the known external login providers. - /// - /// A collection of s for the known external login providers. - public virtual IEnumerable GetExternalAuthenticationSchemes() - { - return Context.Authentication.GetAuthenticationSchemes().Where(d => !string.IsNullOrEmpty(d.DisplayName)); - } - /// /// Gets the external login information for the current login, as an asynchronous operation. /// @@ -556,20 +543,20 @@ public virtual IEnumerable GetExternalAuthenticationS /// for the sign-in attempt. public virtual async Task 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 @@ public virtual async Task GetExternalLoginInfoAsync(string ex } 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 @@ await UserManager.GetTwoFactorEnabledAsync(user) && { // 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 @@ await UserManager.GetTwoFactorEnabledAsync(user) && private async Task 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; diff --git a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/DefaultPocoTest.cs b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/DefaultPocoTest.cs index 83897ce38..2c787bf2d 100644 --- a/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/DefaultPocoTest.cs +++ b/test/Microsoft.AspNetCore.Identity.EntityFrameworkCore.Test/DefaultPocoTest.cs @@ -9,6 +9,7 @@ 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 @@ public DefaultPocoTest(ScratchDatabaseFixture fixture) var services = new ServiceCollection(); services + .AddSingleton(new ConfigurationBuilder().Build()) .AddDbContext(o => o.UseSqlServer(fixture.ConnectionString)) .AddIdentity() .AddEntityFrameworkStores(); diff --git a/test/Microsoft.AspNetCore.Identity.InMemory.Test/ControllerTest.cs b/test/Microsoft.AspNetCore.Identity.InMemory.Test/ControllerTest.cs index b09c626e3..77bae7ed9 100644 --- a/test/Microsoft.AspNetCore.Identity.InMemory.Test/ControllerTest.cs +++ b/test/Microsoft.AspNetCore.Identity.InMemory.Test/ControllerTest.cs @@ -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 @@ public class ControllerTest [InlineData(false)] public async Task VerifyAccountControllerSignIn(bool isPersistent) { - var context = new Mock(); - var auth = new Mock(); - 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(), It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); // REVIEW: is persistant mocking broken //It.Is(v => v.IsPersistent == isPersistent))).Returns(Task.FromResult(0)).Verifiable(); var contextAccessor = new Mock(); - 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(new ConfigurationBuilder().Build()) + .AddLogging() + .AddSingleton(contextAccessor.Object); + services.AddIdentity(); services.AddSingleton, InMemoryStore>(); services.AddSingleton, InMemoryStore>(); var app = new ApplicationBuilder(services.BuildServiceProvider()); - app.UseCookieAuthentication(); // Act var user = new TestUser @@ -61,7 +56,6 @@ public async Task VerifyAccountControllerSignIn(bool isPersistent) // Assert Assert.True(result.Succeeded); - context.VerifyAll(); auth.VerifyAll(); contextAccessor.VerifyAll(); } @@ -83,21 +77,20 @@ public async Task VerifyAccountControllerExternalLoginWithTokensFlow() } }; - var auth = new Mock(); - auth.Setup(a => a.AuthenticateAsync(It.IsAny())).Returns(Task.FromResult(0)); - var context = new Mock(); - 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())).Returns(Task.FromResult(AuthenticateResult.None())); var contextAccessor = new Mock(); - 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(new ConfigurationBuilder().Build()) + .AddLogging() + .AddSingleton(contextAccessor.Object); services.AddIdentity(); services.AddSingleton, InMemoryStore>(); services.AddSingleton, InMemoryStore>(); var app = new ApplicationBuilder(services.BuildServiceProvider()); - app.UseCookieAuthentication(); // Act var user = new TestUser @@ -113,5 +106,12 @@ public async Task VerifyAccountControllerExternalLoginWithTokensFlow() Assert.Equal("refresh", await userManager.GetAuthenticationTokenAsync(user, authScheme, "refresh_token")); Assert.Equal("access", await userManager.GetAuthenticationTokenAsync(user, authScheme, "access_token")); } + + private Mock MockAuth(HttpContext context) + { + var auth = new Mock(); + context.RequestServices = new ServiceCollection().AddSingleton(auth.Object).BuildServiceProvider(); + return auth; + } } } diff --git a/test/Microsoft.AspNetCore.Identity.InMemory.Test/FunctionalTest.cs b/test/Microsoft.AspNetCore.Identity.InMemory.Test/FunctionalTest.cs index 273a3dd91..385f653f2 100644 --- a/test/Microsoft.AspNetCore.Identity.InMemory.Test/FunctionalTest.cs +++ b/test/Microsoft.AspNetCore.Identity.InMemory.Test/FunctionalTest.cs @@ -11,11 +11,11 @@ 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 @@ public class FunctionalTest { const string TestPassword = "1qaz!QAZ"; - [Fact] - public void UseIdentityThrowsWithoutAddIdentity() - { - var builder = new WebHostBuilder() - .Configure(app => app.UseIdentity()); - Assert.Throws(() => new TestServer(builder)); - } - [Fact] public async Task CanChangePasswordOptions() { @@ -57,12 +49,15 @@ public async Task CanChangePasswordOptions() public async Task CanCreateMeLoginAndCookieStopsWorkingAfterExpiration() { var clock = new TestClock(); - var server = CreateServer(services => services.Configure(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(clock); + }); var transaction1 = await SendAsync(server, "http://example.com/createMe"); Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode); @@ -96,10 +91,7 @@ public async Task CanCreateMeLoginAndCookieStopsWorkingAfterExpiration() public async Task CanCreateMeLoginAndSecurityStampExtendsExpiration(bool rememberMe) { var clock = new TestClock(); - var server = CreateServer(services => services.Configure(options => - { - options.Cookies.ApplicationCookie.SystemClock = clock; - })); + var server = CreateServer(services => services.AddSingleton(clock)); var transaction1 = await SendAsync(server, "http://example.com/createMe"); Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode); @@ -143,17 +135,20 @@ public async Task CanCreateMeLoginAndSecurityStampExtendsExpiration(bool remembe public async Task CanAccessOldPrincipalDuringSecurityStampReplacement() { var clock = new TestClock(); - var server = CreateServer(services => services.Configure(options => + var server = CreateServer(services => { - options.Cookies.ApplicationCookie.SystemClock = clock; - options.OnSecurityStampRefreshingPrincipal = c => + services.Configure(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(clock); + }); var transaction1 = await SendAsync(server, "http://example.com/createMe"); Assert.Equal(HttpStatusCode.OK, transaction1.Response.StatusCode); @@ -233,7 +228,7 @@ private static TestServer CreateServer(Action configureServi var builder = new WebHostBuilder() .Configure(app => { - app.UseIdentity(); + app.UseAuthentication(); app.Use(async (context, next) => { var req = context.Request; @@ -282,14 +277,11 @@ private static TestServer CreateServer(Action configureServi } 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 @@ private static TestServer CreateServer(Action configureServi services.AddIdentity(); services.AddSingleton, InMemoryStore>(); services.AddSingleton, InMemoryStore>(); - 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 @@ private static void Describe(HttpResponse res, AuthenticateContext result) } 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()) { diff --git a/test/Microsoft.AspNetCore.Identity.InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj b/test/Microsoft.AspNetCore.Identity.InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj index 28feb37b9..81898b0b6 100644 --- a/test/Microsoft.AspNetCore.Identity.InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj +++ b/test/Microsoft.AspNetCore.Identity.InMemory.Test/Microsoft.AspNetCore.Identity.InMemory.Test.csproj @@ -19,8 +19,6 @@ - - diff --git a/test/Microsoft.AspNetCore.Identity.Test/IdentityBuilderTest.cs b/test/Microsoft.AspNetCore.Identity.Test/IdentityBuilderTest.cs index 31c9ce27f..83804c949 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/IdentityBuilderTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/IdentityBuilderTest.cs @@ -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 @@ public class IdentityBuilderTest [Fact] public void CanOverrideUserStore() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddUserStore(); var thingy = services.BuildServiceProvider().GetRequiredService>() as MyUberThingy; Assert.NotNull(thingy); @@ -31,7 +31,8 @@ public void CanOverrideUserStore() [Fact] public void CanOverrideRoleStore() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddRoleStore(); var thingy = services.BuildServiceProvider().GetRequiredService>() as MyUberThingy; Assert.NotNull(thingy); @@ -40,7 +41,9 @@ public void CanOverrideRoleStore() [Fact] public void CanOverridePrincipalFactory() { - var services = new ServiceCollection().AddLogging(); + var services = new ServiceCollection() + .AddLogging() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity() .AddClaimsPrincipalFactory() .AddUserManager() @@ -53,7 +56,8 @@ public void CanOverridePrincipalFactory() [Fact] public void CanOverrideRoleValidator() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddRoleValidator(); var thingy = services.BuildServiceProvider().GetRequiredService>() as MyUberThingy; Assert.NotNull(thingy); @@ -62,7 +66,8 @@ public void CanOverrideRoleValidator() [Fact] public void CanOverrideUserValidator() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddUserValidator(); var thingy = services.BuildServiceProvider().GetRequiredService>() as MyUberThingy; Assert.NotNull(thingy); @@ -71,7 +76,8 @@ public void CanOverrideUserValidator() [Fact] public void CanOverridePasswordValidator() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddPasswordValidator(); var thingy = services.BuildServiceProvider().GetRequiredService>() as MyUberThingy; Assert.NotNull(thingy); @@ -80,7 +86,8 @@ public void CanOverridePasswordValidator() [Fact] public void CanOverrideUserManager() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity() .AddUserStore() .AddUserManager(); @@ -91,7 +98,8 @@ public void CanOverrideUserManager() [Fact] public void CanOverrideRoleManager() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity() .AddRoleStore() .AddRoleManager(); @@ -102,10 +110,11 @@ public void CanOverrideRoleManager() [Fact] public void CanOverrideSignInManager() { - var services = new ServiceCollection(); - services.AddSingleton() - .AddLogging() - .AddIdentity() + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()) + .AddSingleton() + .AddLogging(); + services.AddIdentity() .AddUserStore() .AddRoleStore() .AddUserManager() @@ -118,7 +127,8 @@ public void CanOverrideSignInManager() [Fact] public void EnsureDefaultServices() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity(); var provider = services.BuildServiceProvider(); @@ -135,7 +145,8 @@ public void EnsureDefaultServices() [Fact] public void EnsureDefaultTokenProviders() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity().AddDefaultTokenProviders(); var provider = services.BuildServiceProvider(); @@ -146,7 +157,8 @@ public void EnsureDefaultTokenProviders() [Fact] public void AddManagerWithWrongTypesThrows() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); var builder = services.AddIdentity(); Assert.Throws(() => builder.AddUserManager>()); Assert.Throws(() => builder.AddRoleManager>()); @@ -159,7 +171,8 @@ public void AddManagerWithWrongTypesThrows() [Fact] public void AddTokenProviderWithWrongTypesThrows() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); var builder = services.AddIdentity(); Assert.Throws(() => builder.AddTokenProvider("whatevs")); Assert.Throws(() => builder.AddTokenProvider("whatevs", typeof(object))); diff --git a/test/Microsoft.AspNetCore.Identity.Test/IdentityOptionsTest.cs b/test/Microsoft.AspNetCore.Identity.Test/IdentityOptionsTest.cs index 3370005dc..d3cca4b97 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/IdentityOptionsTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/IdentityOptionsTest.cs @@ -36,11 +36,6 @@ public void VerifyDefaultOptions() 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 @@ public void IdentityOptionsFromConfig() Assert.Equal(roleClaimType, config["identity:claimsidentity:roleclaimtype"]); var services = new ServiceCollection(); + services.AddSingleton(config); services.AddIdentity(); - services.Configure(config.GetSection("identity")); var accessor = services.BuildServiceProvider().GetRequiredService>(); Assert.NotNull(accessor); var options = accessor.Value; @@ -106,7 +101,7 @@ public void IdentityOptionsActionOverridesConfig() builder.AddInMemoryCollection(dic); var config = builder.Build(); var services = new ServiceCollection(); - services.Configure(config.GetSection("identity")); + services.AddSingleton(config); services.AddIdentity(o => { o.User.RequireUniqueEmail = false; o.Lockout.MaxFailedAccessAttempts++; }); var accessor = services.BuildServiceProvider().GetRequiredService>(); Assert.NotNull(accessor); @@ -119,6 +114,7 @@ public void IdentityOptionsActionOverridesConfig() public void CanCustomizeIdentityOptions() { var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()) .Configure(options => options.Password.RequiredLength = -1); services.AddIdentity(); var serviceProvider = services.BuildServiceProvider(); @@ -139,7 +135,8 @@ public void CanCustomizeIdentityOptions() [Fact] public void CanSetupIdentityOptions() { - var services = new ServiceCollection(); + var services = new ServiceCollection() + .AddSingleton(new ConfigurationBuilder().Build()); services.AddIdentity(options => options.User.RequireUniqueEmail = true); var serviceProvider = services.BuildServiceProvider(); diff --git a/test/Microsoft.AspNetCore.Identity.Test/SecurityStampValidatorTest.cs b/test/Microsoft.AspNetCore.Identity.Test/SecurityStampValidatorTest.cs index 7bdaa811a..b80c2757a 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/SecurityStampValidatorTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/SecurityStampValidatorTest.cs @@ -8,7 +8,6 @@ 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 AuthenticateAsync(AuthenticateContext context) + { + throw new NotImplementedException(); + } + + public Task ChallengeAsync(ChallengeContext context) + { + throw new NotImplementedException(); + } + + public Task 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.Setup(c => c.RequestServices).Returns(new ServiceCollection().BuildServiceProvider()); - var id = new ClaimsPrincipal(new ClaimsIdentity(scheme)); + var id = new ClaimsPrincipal(new ClaimsIdentity(IdentityCookieOptions.ApplicationScheme)); var ticket = new AuthenticationTicket(id, new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow }, scheme); - var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions()); + var context = new CookieValidatePrincipalContext(httpContext.Object, new AuthenticationSchemeBuilder(scheme) { HandlerType = typeof(NoopHandler) }.Build(), ticket, new CookieAuthenticationOptions()); var ex = await Assert.ThrowsAsync(() => SecurityStampValidator.ValidatePrincipalAsync(context)); } @@ -56,13 +87,13 @@ public async Task OnValidatePrincipalTestSuccess(bool isPersistent) var services = new ServiceCollection(); services.AddSingleton(options.Object); services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object)); + services.AddSingleton(new SecurityStampValidator(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 @@ public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails() var services = new ServiceCollection(); services.AddSingleton(options.Object); services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object)); + services.AddSingleton(new SecurityStampValidator(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 @@ public async Task OnValidateIdentityRejectsWhenValidateSecurityStampFails() 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 @@ public async Task OnValidateIdentityRejectsWhenNoIssuedUtc() var services = new ServiceCollection(); services.AddSingleton(options.Object); services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object)); + services.AddSingleton(new SecurityStampValidator(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 @@ public async Task OnValidateIdentityRejectsWhenNoIssuedUtc() 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 @@ public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired() var services = new ServiceCollection(); services.AddSingleton(options.Object); services.AddSingleton(signInManager.Object); - services.AddSingleton(new SecurityStampValidator(options.Object, signInManager.Object)); + services.AddSingleton(new SecurityStampValidator(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 @@ public async Task OnValidateIdentityDoesNotRejectsWhenNotExpired() 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); diff --git a/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs b/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs index 06f630c4b..8a6bc204b 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/SignInManagerTest.cs @@ -7,10 +7,10 @@ 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 @@ public async Task CanPasswordSignIn(bool isPersistent) manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); - var context = new Mock(); - var auth = new Mock(); - 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 @@ public async Task CanPasswordSignIn(bool isPersistent) // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -229,11 +227,10 @@ public async Task CanPasswordSignInWithNoLogger() manager.Setup(m => m.IsLockedOutAsync(user)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); - var context = new Mock(); - var auth = new Mock(); - 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 @@ public async Task CanPasswordSignInWithNoLogger() // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -257,11 +253,10 @@ public async Task PasswordSignInWorksWithNonTwoFactorStore() manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable(); - var context = new Mock(); - var auth = new Mock(); - 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 @@ public async Task PasswordSignInWorksWithNonTwoFactorStore() // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -296,12 +290,12 @@ public async Task PasswordSignInRequiresVerification(bool supportsLockout) { manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable(); } - var context = new Mock(); - var helper = SetupSignInManager(manager.Object, context.Object); - var auth = new Mock(); - auth.Setup(a => a.SignInAsync(helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, - It.Is(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(id => id.FindFirstValue(ClaimTypes.Name) == user.Id), + It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); @@ -310,7 +304,6 @@ public async Task PasswordSignInRequiresVerification(bool supportsLockout) Assert.False(result.Succeeded); Assert.True(result.RequiresTwoFactor); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -334,19 +327,19 @@ public async Task ExternalSignInRequiresVerificationIfNotBypassed(bool bypass) manager.Setup(m => m.SupportsUserTwoFactor).Returns(true).Verifiable(); manager.Setup(m => m.GetTwoFactorEnabledAsync(user)).ReturnsAsync(true).Verifiable(); } - var context = new Mock(); - var auth = new Mock(); - 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(id => id.FindFirstValue(ClaimTypes.Name) == user.Id))).Returns(Task.FromResult(0)).Verifiable(); + auth.Setup(a => a.SignInAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, + It.Is(id => id.FindFirstValue(ClaimTypes.Name) == user.Id), + It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); } // Act @@ -356,7 +349,6 @@ public async Task ExternalSignInRequiresVerificationIfNotBypassed(bool bypass) Assert.Equal(bypass, result.Succeeded); Assert.Equal(!bypass, result.RequiresTwoFactor); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -384,21 +376,21 @@ public async Task CanTwoFactorAuthenticatorSignIn(string providerName, bool isPe 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(); - var auth = new Mock(); + var context = new DefaultHttpContext(); + var auth = MockAuth(context); + var helper = SetupSignInManager(manager.Object, context); var twoFactorInfo = new SignInManager.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(i => i.FindFirstValue(ClaimTypes.Name) == user.Id && i.Identities.First().AuthenticationType == helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme), @@ -411,7 +403,6 @@ public async Task CanTwoFactorAuthenticatorSignIn(string providerName, bool isPe // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -432,28 +423,28 @@ public async Task CanTwoFactorRecoveryCodeSignIn(bool supportsLockout, bool exte { manager.Setup(m => m.ResetAccessFailedCountAsync(user)).ReturnsAsync(IdentityResult.Success).Verifiable(); } - var context = new Mock(); - var auth = new Mock(); + var context = new DefaultHttpContext(); + var auth = MockAuth(context); + var helper = SetupSignInManager(manager.Object, context); var twoFactorInfo = new SignInManager.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(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider && i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id), It.IsAny())).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())).Returns(Task.FromResult(0)).Verifiable(); + auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny())).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 @@ public async Task CanTwoFactorRecoveryCodeSignIn(bool supportsLockout, bool exte // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -484,11 +474,10 @@ public async Task CanExternalSignIn(bool isPersistent, bool supportsLockout) } manager.Setup(m => m.FindByLoginAsync(loginProvider, providerKey)).ReturnsAsync(user).Verifiable(); - var context = new Mock(); - var auth = new Mock(); - 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 @@ public async Task CanExternalSignIn(bool isPersistent, bool supportsLockout) // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -509,9 +497,8 @@ public async Task CanResignIn(bool isPersistent, bool externalLogin) { // Setup var user = new TestUser { UserName = "Foo" }; - var context = new Mock(); - var auth = new Mock(); - 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 @@ public async Task CanResignIn(bool isPersistent, bool externalLogin) } // REVIEW: auth changes we lost the ability to mock is persistent //var properties = new AuthenticationProperties { IsPersistent = isPersistent }; - auth.Setup(a => a.AuthenticateAsync(It.Is(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>(manager.Object, - new HttpContextAccessor { HttpContext = context.Object }, + new HttpContextAccessor { HttpContext = context }, new Mock>().Object, null, null) { CallBase = true }; //signInManager.Setup(s => s.SignInAsync(user, It.Is(p => p.IsPersistent == isPersistent), //externalLogin? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable(); signInManager.Setup(s => s.SignInAsync(user, It.IsAny(), 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 @@ public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bo 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(); - var auth = new Mock(); + var context = new DefaultHttpContext(); + var auth = MockAuth(context); + var helper = SetupSignInManager(manager.Object, context); var twoFactorInfo = new SignInManager.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(i => i.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider && i.FindFirstValue(ClaimTypes.NameIdentifier) == user.Id), It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); // REVIEW: restore ability to test is persistent //It.Is(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())).Returns(Task.FromResult(0)).Verifiable(); + auth.Setup(a => a.SignOutAsync(context, helper.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny())).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(i => i.FindFirstValue(ClaimTypes.Name) == user.Id && i.Identities.First().AuthenticationType == helper.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme), It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); //It.Is(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 @@ public async Task CanTwoFactorSignIn(bool isPersistent, bool supportsLockout, bo // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } @@ -623,23 +609,22 @@ public async Task RememberClientStoresUserId() // Setup var user = new TestUser { UserName = "Foo" }; var manager = SetupUserManager(user); - var context = new Mock(); - var auth = new Mock(); - 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(i => i.FindFirstValue(ClaimTypes.Name) == user.Id && i.Identities.First().AuthenticationType == manager.Object.Options.Cookies.TwoFactorRememberMeCookieAuthenticationScheme), It.Is(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 @@ public async Task RememberBrowserSkipsTwoFactorVerificationSignIn(bool isPersist 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(); - var auth = new Mock(); - 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 @@ public async Task RememberBrowserSkipsTwoFactorVerificationSignIn(bool isPersist // Assert Assert.True(result.Succeeded); manager.Verify(); - context.Verify(); auth.Verify(); } + private Mock MockAuth(HttpContext context) + { + var auth = new Mock(); + context.RequestServices = new ServiceCollection().AddSingleton(auth.Object).BuildServiceProvider(); + return auth; + } + [Theory] [InlineData("Microsoft.AspNetCore.Identity.Authentication.Application")] [InlineData("Foo")] @@ -685,20 +676,18 @@ public async Task SignOutCallsContextResponseSignOut(string authenticationScheme { // Setup var manager = MockHelpers.TestUserManager(); - manager.Options.Cookies.ApplicationCookie.AuthenticationScheme = authenticationScheme; - var context = new Mock(); - var auth = new Mock(); - 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())).Returns(Task.FromResult(0)).Verifiable(); + auth.Setup(a => a.SignOutAsync(context, manager.Options.Cookies.TwoFactorUserIdCookieAuthenticationScheme, It.IsAny())).Returns(Task.FromResult(0)).Verifiable(); + auth.Setup(a => a.SignOutAsync(context, manager.Options.Cookies.ExternalCookieAuthenticationScheme, It.IsAny())).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 @@ public async Task CanRequireConfirmedEmailForPasswordSignIn(bool confirmed) { manager.Setup(m => m.CheckPasswordAsync(user, "password")).ReturnsAsync(true).Verifiable(); } - var context = new Mock(); - var auth = new Mock(); + 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 @@ public async Task CanRequireConfirmedEmailForPasswordSignIn(bool confirmed) 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 auth, string userId = null, bool? isPersistent = null, string loginProvider = null) + private static void SetupSignIn(HttpContext context, Mock 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(id => (userId == null || id.FindFirstValue(ClaimTypes.NameIdentifier) == userId) && (loginProvider == null || id.FindFirstValue(ClaimTypes.AuthenticationMethod) == loginProvider)), @@ -856,19 +844,18 @@ public async Task CanRequireConfirmedPhoneNumberForPasswordSignIn(bool confirmed var user = new TestUser { UserName = "Foo" }; var manager = SetupUserManager(user); manager.Setup(m => m.IsPhoneNumberConfirmedAsync(user)).ReturnsAsync(confirmed).Verifiable(); - var context = new Mock(); - var auth = new Mock(); + 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 @@ public async Task CanRequireConfirmedPhoneNumberForPasswordSignIn(bool confirmed 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(); } } diff --git a/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs b/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs index d38354f20..6370d397e 100644 --- a/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs +++ b/test/Microsoft.AspNetCore.Identity.Test/UserManagerTest.cs @@ -10,6 +10,7 @@ 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 @@ public class UserManagerTest [Fact] public void EnsureDefaultServicesDefaultsWithStoreWorks() { + var config = new ConfigurationBuilder().Build(); var services = new ServiceCollection() + .AddSingleton(config) .AddTransient, NoopUserStore>(); services.AddIdentity(); services.AddSingleton(); @@ -35,7 +38,9 @@ public void EnsureDefaultServicesDefaultsWithStoreWorks() [Fact] public void AddUserManagerWithCustomManagerReturnsSameInstance() { + var config = new ConfigurationBuilder().Build(); var services = new ServiceCollection() + .AddSingleton(config) .AddTransient, NoopUserStore>() .AddSingleton(); @@ -682,10 +687,13 @@ public Task ValidateAsync(string purpose, string token, UserManager(o => o.Tokens.ProviderMap.Add("A", new TokenProviderDescriptor(typeof(ATokenProvider)) + var config = new ConfigurationBuilder().Build(); + var services = new ServiceCollection() + .AddSingleton(config) + .AddLogging(); + + services.AddIdentity(o => o.Tokens.ProviderMap.Add("A", new TokenProviderDescriptor(typeof(ATokenProvider)) { ProviderInstance = provider })).AddUserStore(); @@ -836,10 +844,13 @@ private static char ValueToChar(byte b) [Fact] public void UserManagerWillUseTokenProviderInstanceOverDefaults() { - var services = new ServiceCollection(); var provider = new ATokenProvider(); - services.AddLogging() - .AddIdentity(o => o.Tokens.ProviderMap.Add(TokenOptions.DefaultProvider, new TokenProviderDescriptor(typeof(ATokenProvider)) + var config = new ConfigurationBuilder().Build(); + var services = new ServiceCollection() + .AddSingleton(config) + .AddLogging(); + + services.AddIdentity(o => o.Tokens.ProviderMap.Add(TokenOptions.DefaultProvider, new TokenProviderDescriptor(typeof(ATokenProvider)) { ProviderInstance = provider })).AddUserStore().AddDefaultTokenProviders(); @@ -1668,14 +1679,17 @@ Task IUserStore.DeleteAsync(TestUser user, Cancellatio [Fact] public async Task CanCustomizeUserValidatorErrors() { - var services = new ServiceCollection(); var store = new Mock>(); var describer = new TestErrorDescriber(); - services.AddSingleton(describer) - .AddSingleton>(store.Object) - .AddIdentity(); - services.AddLogging(); - services.AddSingleton(); + var config = new ConfigurationBuilder().Build(); + var services = new ServiceCollection() + .AddSingleton(config) + .AddLogging() + .AddSingleton(describer) + .AddSingleton>(store.Object) + .AddSingleton(); + + services.AddIdentity(); var manager = services.BuildServiceProvider().GetRequiredService>();