// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Threading; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Security; using Microsoft.AspNet.Identity.Test; using Microsoft.AspNet.Security.Cookies; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection.Fallback; using Microsoft.Framework.OptionsModel; using Moq; using System.Security.Claims; using System.Threading.Tasks; using Xunit; using Microsoft.AspNet.Builder; namespace Microsoft.AspNet.Identity.Security.Test { public class ApplicationUser : IdentityUser { } public class HttpSignInTest { #if NET45 [Theory] [InlineData(true)] [InlineData(false)] public async Task VerifyAccountControllerSignIn(bool isPersistent) { IBuilder app = new Builder.Builder(new ServiceCollection().BuildServiceProvider()); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie }); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignIn(It.IsAny(), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); app.UseServices(services => { services.Add(OptionsServices.GetDefaultServices()); services.AddInstance(contextAccessor.Object); services.AddIdentity(s => { s.AddInMemory(); }).AddHttpSignIn(); }); // Act var user = new ApplicationUser { UserName = "Yolo" }; const string password = "Yol0Sw@g!"; var userManager = app.ApplicationServices.GetService>(); var signInManager = app.ApplicationServices.GetService>(); IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); var result = await signInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false); // Assert Assert.Equal(SignInStatus.Success, result); context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } //[Theory] //[InlineData(true)] //[InlineData(false)] //public async Task VerifyAccountControllerSignInFunctional(bool isPersistent) //{ // IBuilder app = new Builder(new ServiceCollection().BuildServiceProvider()); // app.UseCookieAuthentication(new CookieAuthenticationOptions // { // AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie // }); // TODO: how to functionally test context? // var context = new DefaultHttpContext(new FeatureCollection()); // var contextAccessor = new Mock>(); // contextAccessor.Setup(a => a.Value).Returns(context); // app.UseServices(services => // { // services.AddInstance(contextAccessor.Object); // services.AddInstance(new NullLoggerFactory()); // services.AddIdentity(s => // { // s.AddUserStore(() => new InMemoryUserStore()); // s.AddUserManager(); // s.AddRoleStore(() => new InMemoryRoleStore()); // s.AddRoleManager(); // }); // services.AddTransient(); // }); // // Act // var user = new ApplicationUser // { // UserName = "Yolo" // }; // const string password = "Yol0Sw@g!"; // var userManager = app.ApplicationServices.GetService(); // var HttpSignInManager = app.ApplicationServices.GetService(); // IdentityResultAssert.IsSuccess(await userManager.CreateAsync(user, password)); // var result = await HttpSignInManager.PasswordSignInAsync(user.UserName, password, isPersistent, false); // // Assert // Assert.Equal(SignInStatus.Success, result); // contextAccessor.VerifyAll(); //} [Fact] public void ConstructorNullChecks() { Assert.Throws("userManager", () => new SignInManager(null, null)); var userManager = MockHelpers.MockUserManager().Object; Assert.Throws("authenticationManager", () => new SignInManager(userManager, null)); } //TODO: Mock fails in K (this works fine in net45) //[Fact] //public async Task EnsureClaimsIdentityFactoryCreateIdentityCalled() //{ // // Setup // var user = new TestUser { UserName = "Foo" }; // var userManager = MockHelpers.TestUserManager(); // var identityFactory = new Mock>(); // const string authType = "Test"; // var testIdentity = new ClaimsIdentity(authType); // identityFactory.Setup(s => s.CreateAsync(userManager, user, authType, CancellationToken.None)).ReturnsAsync(testIdentity).Verifiable(); // userManager.ClaimsIdentityFactory = identityFactory.Object; // var context = new Mock(); // var response = new Mock(); // context.Setup(c => c.Response).Returns(response.Object).Verifiable(); // response.Setup(r => r.SignIn(testIdentity, It.IsAny())).Verifiable(); // var contextAccessor = new Mock>(); // contextAccessor.Setup(a => a.Value).Returns(context.Object); // var helper = new HttpAuthenticationManager(contextAccessor.Object); // // Act // helper.SignIn(user, false); // // Assert // identityFactory.VerifyAll(); // context.VerifyAll(); // contextAccessor.VerifyAll(); // response.VerifyAll(); //} [Fact] public async Task PasswordSignInReturnsLockedOutWhenLockedOut() { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); // Assert Assert.Equal(SignInStatus.LockedOut, result); manager.VerifyAll(); } [Theory] [InlineData(true)] [InlineData(false)] public async Task CanPasswordSignIn(bool isPersistent) { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity("Microsoft.AspNet.Identity")).Verifiable(); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignIn(It.IsAny(), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); // Assert Assert.Equal(SignInStatus.Success, result); manager.VerifyAll(); context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } [Fact] public async Task PasswordSignInRequiresVerification() { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable(); var context = new Mock(); var response = new Mock(); response.Setup(r => r.SignIn(It.Is(id => id.Name == user.Id))).Verifiable(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", false, false); // Assert Assert.Equal(SignInStatus.RequiresVerification, result); manager.VerifyAll(); context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } [Theory] [InlineData(true)] [InlineData(false)] public async Task CanTwoFactorSignIn(bool isPersistent) { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); var provider = "twofactorprovider"; var code = "123456"; manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByIdAsync(user.Id, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.VerifyTwoFactorTokenAsync(user, provider, code, CancellationToken.None)).ReturnsAsync(true).Verifiable(); var context = new Mock(); var response = new Mock(); response.Setup(r => r.SignIn(It.IsAny(), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType); id.AddClaim(new Claim(ClaimTypes.Name, user.Id)); var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription()); context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorUserIdAuthenticationType)).ReturnsAsync(authResult).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.TwoFactorSignInAsync(provider, code, isPersistent); // Assert Assert.Equal(SignInStatus.Success, result); manager.VerifyAll(); context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } [Fact] public void RememberClientStoresUserId() { // Setup var user = new TestUser { UserName = "Foo" }; var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignIn(It.Is(i => i.AuthenticationType == HttpAuthenticationManager.TwoFactorRememberedAuthenticationType))).Verifiable(); var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType); id.AddClaim(new Claim(ClaimTypes.Name, user.Id)); var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription()); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var signInService = new HttpAuthenticationManager(contextAccessor.Object); // Act signInService.RememberClient(user.Id); // Assert context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } [Theory] [InlineData(true)] [InlineData(false)] public async Task RememberBrowserSkipsTwoFactorVerificationSignIn(bool isPersistent) { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.GetTwoFactorEnabledAsync(user, CancellationToken.None)).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.GetUserIdAsync(user, CancellationToken.None)).ReturnsAsync(user.Id).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "password", CancellationToken.None)).ReturnsAsync(true).Verifiable(); manager.Setup(m => m.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie, CancellationToken.None)).ReturnsAsync(new ClaimsIdentity(DefaultAuthenticationTypes.ApplicationCookie)).Verifiable(); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignIn(It.Is(i => i.AuthenticationType == DefaultAuthenticationTypes.ApplicationCookie), It.Is(v => v.IsPersistent == isPersistent))).Verifiable(); var id = new ClaimsIdentity(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType); id.AddClaim(new Claim(ClaimTypes.Name, user.Id)); var authResult = new AuthenticationResult(id, new AuthenticationProperties(), new AuthenticationDescription()); context.Setup(c => c.AuthenticateAsync(HttpAuthenticationManager.TwoFactorRememberedAuthenticationType)).ReturnsAsync(authResult).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var signInService = new HttpAuthenticationManager(contextAccessor.Object); var helper = new SignInManager(manager.Object, signInService); // Act var result = await helper.PasswordSignInAsync(user.UserName, "password", isPersistent, false); // Assert Assert.Equal(SignInStatus.Success, result); manager.VerifyAll(); context.VerifyAll(); response.VerifyAll(); contextAccessor.VerifyAll(); } [Theory] [InlineData("Microsoft.AspNet.Identity.Security.Application")] [InlineData("Foo")] public void SignOutCallsContextResponseSignOut(string authenticationType) { // Setup var manager = MockHelpers.MockUserManager(); var context = new Mock(); var response = new Mock(); context.Setup(c => c.Response).Returns(response.Object).Verifiable(); response.Setup(r => r.SignOut(authenticationType)).Verifiable(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)) { AuthenticationType = authenticationType }; // Act helper.SignOut(); // Assert context.VerifyAll(); response.VerifyAll(); } [Fact] public async Task PasswordSignInFailsWithWrongPassword() { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).ReturnsAsync(false).Verifiable(); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable(); var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, false); // Assert Assert.Equal(SignInStatus.Failure, result); manager.VerifyAll(); } [Fact] public async Task PasswordSignInFailsWithUnknownUser() { // Setup var manager = MockHelpers.MockUserManager(); manager.Setup(m => m.FindByNameAsync("bogus", CancellationToken.None)).ReturnsAsync(null).Verifiable(); var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync("bogus", "bogus", false, false); // Assert Assert.Equal(SignInStatus.Failure, result); manager.VerifyAll(); } [Fact] public async Task PasswordSignInFailsWithWrongPasswordCanAccessFailedAndLockout() { // Setup var user = new TestUser { UserName = "Foo" }; var manager = MockHelpers.MockUserManager(); var lockedout = false; manager.Setup(m => m.AccessFailedAsync(user, CancellationToken.None)).Returns(() => { lockedout = true; return Task.FromResult(IdentityResult.Success); }).Verifiable(); manager.Setup(m => m.SupportsUserLockout).Returns(true).Verifiable(); manager.Setup(m => m.IsLockedOutAsync(user, CancellationToken.None)).Returns(() => Task.FromResult(lockedout)); manager.Setup(m => m.FindByNameAsync(user.UserName, CancellationToken.None)).ReturnsAsync(user).Verifiable(); manager.Setup(m => m.CheckPasswordAsync(user, "bogus", CancellationToken.None)).ReturnsAsync(false).Verifiable(); var context = new Mock(); var contextAccessor = new Mock>(); contextAccessor.Setup(a => a.Value).Returns(context.Object); var helper = new SignInManager(manager.Object, new HttpAuthenticationManager(contextAccessor.Object)); // Act var result = await helper.PasswordSignInAsync(user.UserName, "bogus", false, true); // Assert Assert.Equal(SignInStatus.LockedOut, result); manager.VerifyAll(); } #endif } }