From dc954e1936ab4e0a8d08a7568e3271dd928431b8 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 25 Jun 2019 17:51:43 -0700 Subject: [PATCH] Fix external login registration when RequireConfirmedAccount (#11572) --- .../Pages/V3/Account/ExternalLogin.cshtml.cs | 8 +++- .../Pages/V4/Account/ExternalLogin.cshtml.cs | 9 +++- .../Pages/Account/ExternalLogin.cs | 22 +++++++-- .../RegistrationTests.cs | 46 +++++++++++++++++++ .../Identity.FunctionalTests/UserStories.cs | 13 ++++++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ExternalLogin.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ExternalLogin.cshtml.cs index e759ae515e..aee48019f4 100644 --- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ExternalLogin.cshtml.cs +++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ExternalLogin.cshtml.cs @@ -193,9 +193,15 @@ namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal result = await _userManager.AddLoginAsync(user, info); if (result.Succeeded) { - await _signInManager.SignInAsync(user, isPersistent: false); _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); + // If account confirmation is required, we need to show the link if we don't have a real email sender + if (_userManager.Options.SignIn.RequireConfirmedAccount) + { + return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email }); + } + + await _signInManager.SignInAsync(user, isPersistent: false); var userId = await _userManager.GetUserIdAsync(user); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Page( diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ExternalLogin.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ExternalLogin.cshtml.cs index 43ab958149..42c2d2ba53 100644 --- a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ExternalLogin.cshtml.cs +++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ExternalLogin.cshtml.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Identity.UI.Services; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal { @@ -192,9 +193,15 @@ namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal result = await _userManager.AddLoginAsync(user, info); if (result.Succeeded) { - await _signInManager.SignInAsync(user, isPersistent: false); _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider); + // If account confirmation is required, we need to show the link if we don't have a real email sender + if (_userManager.Options.SignIn.RequireConfirmedAccount) + { + return RedirectToPage("./RegisterConfirmation", new { Email = Input.Email }); + } + + await _signInManager.SignInAsync(user, isPersistent: false); var userId = await _userManager.GetUserIdAsync(user); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); var callbackUrl = Url.Page( diff --git a/src/Identity/test/Identity.FunctionalTests/Pages/Account/ExternalLogin.cs b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ExternalLogin.cs index c05bcd7f5f..dcec43485b 100644 --- a/src/Identity/test/Identity.FunctionalTests/Pages/Account/ExternalLogin.cs +++ b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ExternalLogin.cs @@ -1,6 +1,7 @@ -// 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 System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -35,11 +36,24 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests.Account { ["Input_Email"] = email }); - var goToIndex = ResponseAssert.IsRedirect(response); - var indexResponse = await Client.GetAsync(goToIndex); + var redirect = ResponseAssert.IsRedirect(response); + var indexResponse = await Client.GetAsync(redirect); var index = await ResponseAssert.IsHtmlDocumentAsync(indexResponse); - return new Index(Client, index, Context.WithAuthenticatedUser()); } + + public async Task SendEmailWithConfirmationAsync(string email, bool hasRealEmail) + { + var response = await Client.SendAsync(_emailForm, new Dictionary + { + ["Input_Email"] = email + }); + var redirect = ResponseAssert.IsRedirect(response); + Assert.True(String.Equals(RegisterConfirmation.Path + "?email=" + email, redirect.ToString(), StringComparison.OrdinalIgnoreCase)); + var registerResponse = await Client.GetAsync(redirect); + var register = await ResponseAssert.IsHtmlDocumentAsync(registerResponse); + + return new RegisterConfirmation(Client, register, hasRealEmail ? Context.WithRealEmailSender() : Context); + } } } diff --git a/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs b/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs index 905a7abc8b..760b36088f 100644 --- a/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs +++ b/src/Identity/test/Identity.FunctionalTests/RegistrationTests.cs @@ -129,6 +129,52 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests await UserStories.RegisterNewUserWithSocialLoginAsync(client, userName, email); } + [Fact] + public async Task CanRegisterWithASocialLoginProviderFromLoginWithConfirmation() + { + // Arrange + void ConfigureTestServices(IServiceCollection services) + { + services.Configure(o => o.SignIn.RequireConfirmedAccount = true) + .SetupTestThirdPartyLogin(); + } + + var client = ServerFactory + .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) + .CreateClient(); + + var guid = Guid.NewGuid(); + var userName = $"{guid}"; + var email = $"{guid}@example.com"; + + // Act & Assert + await UserStories.RegisterNewUserWithSocialLoginWithConfirmationAsync(client, userName, email); + } + + [Fact] + public async Task CanRegisterWithASocialLoginProviderFromLoginWithConfirmationAndRealEmailSender() + { + // Arrange + void ConfigureTestServices(IServiceCollection services) + { + services.AddSingleton(); + services + .Configure(o => o.SignIn.RequireConfirmedAccount = true) + .SetupTestThirdPartyLogin(); + } + + var client = ServerFactory + .WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices)) + .CreateClient(); + + var guid = Guid.NewGuid(); + var userName = $"{guid}"; + var email = $"{guid}@example.com"; + + // Act & Assert + await UserStories.RegisterNewUserWithSocialLoginWithConfirmationAsync(client, userName, email, hasRealEmailSender: true); + } + [Fact] public async Task CanRegisterWithASocialLoginProviderFromRegister() { diff --git a/src/Identity/test/Identity.FunctionalTests/UserStories.cs b/src/Identity/test/Identity.FunctionalTests/UserStories.cs index 9944168a2b..2cc1cde822 100644 --- a/src/Identity/test/Identity.FunctionalTests/UserStories.cs +++ b/src/Identity/test/Identity.FunctionalTests/UserStories.cs @@ -80,6 +80,19 @@ namespace Microsoft.AspNetCore.Identity.FunctionalTests return await externalLogin.SendEmailAsync(email); } + internal static async Task RegisterNewUserWithSocialLoginWithConfirmationAsync(HttpClient client, string userName, string email, bool hasRealEmailSender = false) + { + var index = await Index.CreateAsync(client, new DefaultUIContext().WithSocialLoginEnabled()); + + var login = await index.ClickLoginLinkAsync(); + + var contosoLogin = await login.ClickLoginWithContosoLinkAsync(); + + var externalLogin = await contosoLogin.SendNewUserNameAsync(userName); + + return await externalLogin.SendEmailWithConfirmationAsync(email, hasRealEmailSender); + } + internal static async Task RegisterNewUserWithSocialLoginAsyncViaRegisterPage(HttpClient client, string userName, string email) { var index = await Index.CreateAsync(client, new DefaultUIContext().WithSocialLoginEnabled());