Use RefreshSignInAsync (#1655)

Addresses #1616
This commit is contained in:
Jass Bagga 2018-03-16 10:05:13 -07:00 committed by GitHub
parent 329eed9e8d
commit 641dfe3b62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 468 additions and 15 deletions

View File

@ -4,7 +4,7 @@
namespace Microsoft.AspNetCore.Identity
{
/// <summary>
/// Represents all the options you can use to configure the cookies middleware uesd by the identity system.
/// Represents all the options you can use to configure the cookies middleware used by the identity system.
/// </summary>
public class IdentityConstants
{

View File

@ -8,7 +8,7 @@
@Html.Partial("_StatusMessage", Model.StatusMessage)
<div class="row">
<div class="col-md-6">
<form method="post">
<form id="change-password-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.OldPassword"></label>

View File

@ -99,7 +99,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return Page();
}
await _signInManager.SignInAsync(user, isPersistent: false);
await _signInManager.RefreshSignInAsync(user);
_logger.LogInformation("User changed their password successfully.");
StatusMessage = "Your password has been changed.";

View File

@ -17,7 +17,7 @@
<td>
@if (Model.ShowRemoveButton)
{
<form asp-page-handler="RemoveLogin" method="post">
<form id="remove-login" asp-page-handler="RemoveLogin" method="post">
<div>
<input asp-for="@login.LoginProvider" name="LoginProvider" type="hidden" />
<input asp-for="@login.ProviderKey" name="ProviderKey" type="hidden" />
@ -39,12 +39,12 @@
{
<h4>Add another service to log in.</h4>
<hr />
<form asp-page-handler="LinkLogin" method="post" class="form-horizontal">
<form id="link-login-form" asp-page-handler="LinkLogin" method="post" class="form-horizontal">
<div id="socialLoginList">
<p>
@foreach (var provider in Model.OtherLogins)
{
<button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
<button id="link-login-button" type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
}
</p>
</div>

View File

@ -75,7 +75,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
throw new InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'.");
}
await _signInManager.SignInAsync(user, isPersistent: false);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "The external login was removed.";
return RedirectToPage();
}

View File

@ -18,7 +18,7 @@
</p>
</div>
<div>
<form method="post" class="form-group">
<button class="btn btn-danger" type="submit">Reset authenticator key</button>
<form id="reset-authenticator-form" method="post" class="form-group">
<button id="reset-authenticator-button" class="btn btn-danger" type="submit">Reset authenticator key</button>
</form>
</div>

View File

@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id);
await _signInManager.SignInAsync(user, isPersistent: false);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.";
return RedirectToPage("./EnableAuthenticator");

View File

@ -13,7 +13,7 @@
</p>
<div class="row">
<div class="col-md-6">
<form method="post">
<form id="set-password-form" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.NewPassword"></label>

View File

@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return Page();
}
await _signInManager.SignInAsync(user, isPersistent: false);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your password has been set.";
return RedirectToPage();

View File

@ -3,7 +3,7 @@
<li class="@ManageNavPages.ChangePasswordNavClass(ViewContext)"><a id="change-password" asp-page="./ChangePassword">Password</a></li>
@if ((bool)ViewData["ManageNav.HasExternalLogins"])
{
<li id="external-logins" class="@ManageNavPages.ExternalLoginsNavClass(ViewContext)"><a asp-page="./ExternalLogins">External logins</a></li>
<li id="external-logins" class="@ManageNavPages.ExternalLoginsNavClass(ViewContext)"><a id="external-login" asp-page="./ExternalLogins">External logins</a></li>
}
<li class="@ManageNavPages.TwoFactorAuthenticationNavClass(ViewContext)"><a id="two-factor" asp-page="./TwoFactorAuthentication">Two-factor authentication</a></li>
<li class="@ManageNavPages.PersonalDataNavClass(ViewContext)"><a id="personal-data" asp-page="./PersonalData">Personal data</a></li>

View File

@ -1,7 +1,11 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Identity.DefaultUI.WebSite;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.UI.Services;
@ -25,6 +29,13 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
public static IServiceCollection SetupTestEmailSender(this IServiceCollection services, IEmailSender sender) =>
services.AddSingleton(sender);
public static IServiceCollection SetupGetUserClaimsPrincipal(this IServiceCollection services, Action<ClaimsPrincipal> captureUser, string schemeName) =>
services.Configure<CookieAuthenticationOptions>(schemeName, o => o.Events.OnSigningIn = context =>
{
captureUser(context.Principal);
return Task.CompletedTask;
});
public static IServiceCollection SetupEmailRequired(this IServiceCollection services) =>
services.Configure<IdentityOptions>(o => o.SignIn.RequireConfirmedEmail = true);

View File

@ -2,8 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Identity.DefaultUI.WebSite;
using Microsoft.AspNetCore.TestHost;
using Xunit;
namespace Microsoft.AspNetCore.Identity.FunctionalTests
@ -53,6 +57,124 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
await UserStories.ConfirmEmailAsync(email, client);
}
[Fact]
public async Task CanChangePassword()
{
// Arrange
var principals = new List<ClaimsPrincipal>();
var server = ServerFactory.CreateServer(builder =>
builder.ConfigureTestServices(s => s.SetupGetUserClaimsPrincipal(user => principals.Add(user), IdentityConstants.ApplicationScheme)));
var client = ServerFactory.CreateDefaultClient(server);
var newClient = ServerFactory.CreateDefaultClient(server);
var userName = $"{Guid.NewGuid()}@example.com";
var password = "!Test.Password1";
var index = await UserStories.RegisterNewUserAsync(client, userName, password);
// Act 1
var changedPassword = await UserStories.ChangePasswordAsync(index, "!Test.Password1", "!Test.Password2");
// Assert 1
// RefreshSignIn generates a new security stamp claim
AssertClaimsNotEqual(principals[0], principals[1], "AspNet.Identity.SecurityStamp");
// Act 2
await UserStories.LoginExistingUserAsync(newClient, userName, "!Test.Password2");
// Assert 2
// Signing in again with a different client uses the same security stamp claim
AssertClaimsEqual(principals[1], principals[2], "AspNet.Identity.SecurityStamp");
}
[Fact]
public async Task CanSetPasswordWithExternalLogin()
{
// Arrange
var principals = new List<ClaimsPrincipal>();
var server = ServerFactory.CreateServer(builder =>
builder.ConfigureTestServices(s => s.SetupTestThirdPartyLogin()
.SetupGetUserClaimsPrincipal(user => principals.Add(user), IdentityConstants.ApplicationScheme)));
var client = ServerFactory.CreateDefaultClient(server);
var newClient = ServerFactory.CreateDefaultClient(server);
var guid = Guid.NewGuid();
var userName = $"{guid}";
var email = $"{guid}@example.com";
// Act 1
var index = await UserStories.RegisterNewUserWithSocialLoginAsync(client, userName, email);
index = await UserStories.LoginWithSocialLoginAsync(newClient, userName);
// Assert 1
Assert.NotNull(principals[1].Identities.Single().Claims.Single(c => c.Type == ClaimTypes.AuthenticationMethod).Value);
// Act 2
await UserStories.SetPasswordAsync(index, "!Test.Password2");
// Assert 2
// RefreshSignIn uses the same AuthenticationMethod claim value
AssertClaimsEqual(principals[1], principals[2], ClaimTypes.AuthenticationMethod);
// Act & Assert 3
// Can log in with the password set above
await UserStories.LoginExistingUserAsync(ServerFactory.CreateDefaultClient(server), email, "!Test.Password2");
}
[Fact]
public async Task CanRemoveExternalLogin()
{
// Arrange
var principals = new List<ClaimsPrincipal>();
var server = ServerFactory.CreateServer(builder =>
builder.ConfigureTestServices(s => s.SetupTestThirdPartyLogin()
.SetupGetUserClaimsPrincipal(user => principals.Add(user), IdentityConstants.ApplicationScheme)));
var client = ServerFactory.CreateDefaultClient(server);
var guid = Guid.NewGuid();
var userName = $"{guid}";
var email = $"{guid}@example.com";
// Act
var index = await UserStories.RegisterNewUserAsync(client, email, "!TestPassword1");
var linkLogin = await UserStories.LinkExternalLoginAsync(index, email);
await UserStories.RemoveExternalLoginAsync(linkLogin, email);
// RefreshSignIn generates a new security stamp claim
AssertClaimsNotEqual(principals[0], principals[1], "AspNet.Identity.SecurityStamp");
}
[Fact]
public async Task CanResetAuthenticator()
{
// Arrange
var principals = new List<ClaimsPrincipal>();
var server = ServerFactory.CreateServer(builder =>
builder.ConfigureTestServices(s => s.SetupTestThirdPartyLogin()
.SetupGetUserClaimsPrincipal(user => principals.Add(user), IdentityConstants.ApplicationScheme)));
var client = ServerFactory.CreateDefaultClient(server);
var newClient = ServerFactory.CreateDefaultClient(server);
var userName = $"{Guid.NewGuid()}@example.com";
var password = $"!Test.Password1$";
// Act
var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
var showRecoveryCodes = await UserStories.EnableTwoFactorAuthentication(loggedIn);
var twoFactorKey = showRecoveryCodes.Context.AuthenticatorKey;
// Use a new client to simulate a new browser session.
var index = await UserStories.LoginExistingUser2FaAsync(newClient, userName, password, twoFactorKey);
await UserStories.ResetAuthenticator(index);
// RefreshSignIn generates a new security stamp claim
AssertClaimsNotEqual(principals[1], principals[2], "AspNet.Identity.SecurityStamp");
}
[Fact]
public async Task CanDownloadPersonalData()
{
@ -89,5 +211,19 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
// Act & Assert
await UserStories.DeleteUser(index, password);
}
private void AssertClaimsEqual(ClaimsPrincipal expectedPrincipal, ClaimsPrincipal actualPrincipal, string claimType)
{
var expectedPrincipalClaim = expectedPrincipal.Identities.Single().Claims.Single(c => c.Type == claimType).Value;
var actualPrincipalClaim = actualPrincipal.Identities.Single().Claims.Single(c => c.Type == claimType).Value;
Assert.Equal(expectedPrincipalClaim, actualPrincipalClaim);
}
private void AssertClaimsNotEqual(ClaimsPrincipal expectedPrincipal, ClaimsPrincipal actualPrincipal, string claimType)
{
var expectedPrincipalClaim = expectedPrincipal.Identities.Single().Claims.Single(c => c.Type == claimType).Value;
var actualPrincipalClaim = actualPrincipal.Identities.Single().Claims.Single(c => c.Type == claimType).Value;
Assert.NotEqual(expectedPrincipalClaim, actualPrincipalClaim);
}
}
}

View File

@ -0,0 +1,33 @@
// 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.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class ChangePassword : DefaultUIPage
{
private readonly IHtmlFormElement _changePasswordForm;
public ChangePassword(HttpClient client, IHtmlDocument changePassword, DefaultUIContext context)
: base(client, changePassword, context)
{
_changePasswordForm = HtmlAssert.HasForm("#change-password-form", changePassword);
}
public async Task<ChangePassword> ChangePasswordAsync(string oldPassword, string newPassword)
{
await Client.SendAsync(_changePasswordForm, new Dictionary<string, string>
{
["Input_OldPassword"] = oldPassword,
["Input_NewPassword"] = newPassword,
["Input_ConfirmPassword"] = newPassword
});
return this;
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
@ -14,6 +13,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
private readonly IHtmlAnchorElement _profileLink;
private readonly IHtmlAnchorElement _changePasswordLink;
private readonly IHtmlAnchorElement _twoFactorLink;
private readonly IHtmlAnchorElement _externalLoginLink;
private readonly IHtmlAnchorElement _personalDataLink;
private readonly IHtmlFormElement _updateProfileForm;
private readonly IHtmlElement _confirmEmailButton;
@ -27,6 +27,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
_profileLink = HtmlAssert.HasLink("#profile", manage);
_changePasswordLink = HtmlAssert.HasLink("#change-password", manage);
_twoFactorLink = HtmlAssert.HasLink("#two-factor", manage);
if (Context.ContosoLoginEnabled)
{
_externalLoginLink = HtmlAssert.HasLink("#external-login", manage);
}
_personalDataLink = HtmlAssert.HasLink("#personal-data", manage);
_updateProfileForm = HtmlAssert.HasForm("#profile-form", manage);
if (!Context.EmailConfirmed)
@ -43,6 +47,14 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
return new TwoFactorAuthentication(Client, twoFactor, Context);
}
public async Task<TwoFactorAuthentication> ClickTwoFactorEnabledLinkAsync()
{
var goToTwoFactor = await Client.GetAsync(_twoFactorLink.Href);
var twoFactor = await ResponseAssert.IsHtmlDocumentAsync(goToTwoFactor);
Context.TwoFactorEnabled = true;
return new TwoFactorAuthentication(Client, twoFactor, Context);
}
internal async Task<Index> SendConfirmationEmailAsync()
{
Assert.False(Context.EmailConfirmed);
@ -55,6 +67,22 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
return new Index(Client, manage, Context);
}
public async Task<ChangePassword> ClickChangePasswordLinkAsync()
{
var goToChangePassword = await Client.GetAsync(_changePasswordLink.Href);
var changePasswordDocument = await ResponseAssert.IsHtmlDocumentAsync(goToChangePassword);
return new ChangePassword(Client, changePasswordDocument, Context);
}
public async Task<SetPassword> ClickChangePasswordLinkExternalLoginAsync()
{
var response = await Client.GetAsync(_changePasswordLink.Href);
var goToSetPassword = ResponseAssert.IsRedirect(response);
var setPasswordResponse = await Client.GetAsync(goToSetPassword);
var setPasswordDocument = await ResponseAssert.IsHtmlDocumentAsync(setPasswordResponse);
return new SetPassword(Client, setPasswordDocument, Context);
}
public async Task<PersonalData> ClickPersonalDataLinkAsync()
{
var goToPersonalData = await Client.GetAsync(_personalDataLink.Href);
@ -62,5 +90,12 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
return new PersonalData(Client, personalData, Context);
}
public async Task<LinkExternalLogin> ClickLinkLoginAsync()
{
var goToExternalLogin = await Client.GetAsync(_externalLoginLink.Href);
var externalLoginDocument = await ResponseAssert.IsHtmlDocumentAsync(goToExternalLogin);
return new LinkExternalLogin(Client, externalLoginDocument, Context);
}
}
}

View File

@ -0,0 +1,39 @@
// 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.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class LinkExternalLogin : DefaultUIPage
{
private readonly IHtmlFormElement _linkLoginForm;
private readonly IHtmlElement _linkLoginButton;
public LinkExternalLogin(HttpClient client, IHtmlDocument externalLoginsDocument, DefaultUIContext context)
: base(client, externalLoginsDocument, context)
{
_linkLoginForm = HtmlAssert.HasForm("#link-login-form", externalLoginsDocument);
_linkLoginButton = HtmlAssert.HasElement("#link-login-button", externalLoginsDocument);
}
public async Task<ManageExternalLogin> LinkExternalLoginAsync(string loginEmail)
{
// Click on the button to link external login to current user account
var linkExternalLogin = await Client.SendAsync(_linkLoginForm, _linkLoginButton);
var goToLinkExternalLogin = ResponseAssert.IsRedirect(linkExternalLogin);
var externalLoginResponse = await Client.GetAsync(goToLinkExternalLogin);
var externalLoginDocument = await ResponseAssert.IsHtmlDocumentAsync(externalLoginResponse);
// Redirected to manage page for external login with a remove button
return new ManageExternalLogin(Client, externalLoginDocument, Context);
}
public RemoveExternalLogin ClickRemoveLoginAsync(IHtmlDocument linkedExternalLoginDocument)
{
return new RemoveExternalLogin(Client, linkedExternalLoginDocument, Context);
}
}
}

View File

@ -0,0 +1,37 @@
// 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.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class ManageExternalLogin : DefaultUIPage
{
private readonly IHtmlFormElement _externalLoginForm;
public ManageExternalLogin(HttpClient client, IHtmlDocument externalLoginDocument, DefaultUIContext context)
: base(client, externalLoginDocument, context)
{
_externalLoginForm = HtmlAssert.HasForm("#external-login", externalLoginDocument);
}
public async Task<RemoveExternalLogin> ManageExternalLoginAsync(string loginEmail)
{
var linkedExternalLogin = await Client.SendAsync(_externalLoginForm, new Dictionary<string, string>
{
["Input_Login"] = loginEmail
});
var goToLinkedExternalLogin = ResponseAssert.IsRedirect(linkedExternalLogin);
var externalLoginResponse = await Client.GetAsync(goToLinkedExternalLogin);
var goToManageExternalLogin = ResponseAssert.IsRedirect(externalLoginResponse);
var manageExternalLoginResponse = await Client.GetAsync(goToManageExternalLogin);
var manageExternalLoginDocument = await ResponseAssert.IsHtmlDocumentAsync(manageExternalLoginResponse);
return new RemoveExternalLogin(Client, manageExternalLoginDocument, Context);
}
}
}

View File

@ -0,0 +1,32 @@
// 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.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class RemoveExternalLogin : DefaultUIPage
{
private readonly IHtmlFormElement _removeLoginForm;
public RemoveExternalLogin(HttpClient client, IHtmlDocument externalLogin, DefaultUIContext context)
: base(client, externalLogin, context)
{
_removeLoginForm = HtmlAssert.HasForm("#remove-login", externalLogin);
}
public async Task<RemoveExternalLogin> RemoveLoginAsync(string loginProvider, string providerKey)
{
await Client.SendAsync(_removeLoginForm, new Dictionary<string, string>
{
["login_LoginProvider"] = loginProvider,
["login_ProviderKey"] = providerKey
});
return this;
}
}
}

View File

@ -0,0 +1,33 @@
// 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.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
using Xunit;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class ResetAuthenticator : DefaultUIPage
{
private readonly IHtmlFormElement _resetAuthenticatorForm;
private readonly IHtmlElement _resetAuthenticatorButton;
public ResetAuthenticator(
HttpClient client,
IHtmlDocument resetAuthenticator,
DefaultUIContext context)
: base(client, resetAuthenticator, context)
{
Assert.True(Context.UserAuthenticated);
_resetAuthenticatorForm = HtmlAssert.HasForm("#reset-authenticator-form", resetAuthenticator);
_resetAuthenticatorButton = HtmlAssert.HasElement("#reset-authenticator-button", resetAuthenticator);
}
internal async Task<ResetAuthenticator> ResetAuthenticatorAsync()
{
await Client.SendAsync(_resetAuthenticatorForm, _resetAuthenticatorButton);
return this;
}
}
}

View File

@ -0,0 +1,32 @@
// 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.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using AngleSharp.Dom.Html;
namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
public class SetPassword : DefaultUIPage
{
private readonly IHtmlFormElement _setPasswordForm;
public SetPassword(HttpClient client, IHtmlDocument setPassword, DefaultUIContext context)
: base(client, setPassword, context)
{
_setPasswordForm = HtmlAssert.HasForm("#set-password-form", setPassword);
}
public async Task<SetPassword> SetPasswordAsync(string newPassword)
{
await Client.SendAsync(_setPasswordForm, new Dictionary<string, string>
{
["Input_NewPassword"] = newPassword,
["Input_ConfirmPassword"] = newPassword
});
return this;
}
}
}

View File

@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
public class TwoFactorAuthentication : DefaultUIPage
{
private readonly IHtmlAnchorElement _enableAuthenticatorLink;
private readonly IHtmlAnchorElement _resetAuthenticatorLink;
public TwoFactorAuthentication(HttpClient client, IHtmlDocument twoFactor, DefaultUIContext context)
: base(client, twoFactor, context)
@ -19,6 +20,10 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
{
_enableAuthenticatorLink = HtmlAssert.HasLink("#enable-authenticator", twoFactor);
}
else
{
_resetAuthenticatorLink = HtmlAssert.HasLink("#reset-authenticator", twoFactor);
}
}
internal async Task<EnableAuthenticator> ClickEnableAuthenticatorLinkAsync()
@ -30,5 +35,13 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account.Manage
return new EnableAuthenticator(Client, enableAuthenticator, Context);
}
internal async Task<ResetAuthenticator> ClickResetAuthenticatorLinkAsync()
{
var goToResetAuthenticator = await Client.GetAsync(_resetAuthenticatorLink.Href);
var resetAuthenticator = await ResponseAssert.IsHtmlDocumentAsync(goToResetAuthenticator);
return new ResetAuthenticator(Client, resetAuthenticator, Context);
}
}
}

View File

@ -70,5 +70,16 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
return new Account.Manage.Index(Client, manage, Context);
}
internal async Task<Account.Manage.Index> ClickManageLinkWithExternalLoginAsync()
{
Assert.True(Context.UserAuthenticated);
var goToManage = await Client.GetAsync(_manageLink.Href);
var manage = await ResponseAssert.IsHtmlDocumentAsync(goToManage);
Context.ContosoLoginEnabled = true;
return new Account.Manage.Index(Client, manage, Context);
}
}
}

View File

@ -87,6 +87,14 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
return await enableAuthenticator.SendValidCodeAsync();
}
internal static async Task<ResetAuthenticator> ResetAuthenticator(Index index)
{
var manage = await index.ClickManageLinkAsync();
var twoFactor = await manage.ClickTwoFactorEnabledLinkAsync();
var resetAuthenticator = await twoFactor.ClickResetAuthenticatorLinkAsync();
return await resetAuthenticator.ResetAuthenticatorAsync();
}
internal static async Task<Index> LoginExistingUserRecoveryCodeAsync(
HttpClient client,
string userName,
@ -136,6 +144,39 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests
return await resetPassword.SendNewPasswordAsync(email, newPassword);
}
internal static async Task<ChangePassword> ChangePasswordAsync(Index index, string oldPassword, string newPassword)
{
var manage = await index.ClickManageLinkAsync();
var changePassword = await manage.ClickChangePasswordLinkAsync();
return await changePassword.ChangePasswordAsync(oldPassword, newPassword);
}
internal static async Task<SetPassword> SetPasswordAsync(Index index, string newPassword)
{
var manage = await index.ClickManageLinkAsync();
var setPassword = await manage.ClickChangePasswordLinkExternalLoginAsync();
return await setPassword.SetPasswordAsync(newPassword);
}
internal static async Task<ManageExternalLogin> LinkExternalLoginAsync(Index index, string loginEmail)
{
var manage = await index.ClickManageLinkWithExternalLoginAsync();
var linkLogin = await manage.ClickLinkLoginAsync();
return await linkLogin.LinkExternalLoginAsync(loginEmail);
}
internal static async Task<RemoveExternalLogin> RemoveExternalLoginAsync(ManageExternalLogin manageExternalLogin, string loginEmail)
{
// Provide an email to link an external account to
var removeLogin = await manageExternalLogin.ManageExternalLoginAsync(loginEmail);
// Remove external login
return await removeLogin.RemoveLoginAsync("Contoso", "Contoso");
}
internal static async Task<Index> DeleteUser(Index index, string password)
{
var manage = await index.ClickManageLinkAsync();

View File

@ -9,7 +9,7 @@
<div class="row">
<div class="col-md-4">
<section>
<form method="post">
<form id="external-login" method="post">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Input.Login"></label>