From 32540d570777b29a785a6eaaed16962bffd56350 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Wed, 18 Sep 2019 14:14:31 -0700
Subject: [PATCH 1/7] Fix resend email confirmation
Update src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
Co-Authored-By: campersau
Resend not reset
Update ref
Add new email confirmation link to templates test
Fix test
Revert website change
---
...osoft.AspNetCore.Identity.UI.netcoreapp.cs | 32 ++++++
.../Identity/Pages/V3/Account/Login.cshtml | 2 +-
.../Identity/Pages/V3/Account/Login.cshtml.cs | 29 -----
.../V3/Account/ResendEmailConfirmation.cshtml | 26 +++++
.../Account/ResendEmailConfirmation.cshtml.cs | 103 ++++++++++++++++++
.../Identity/Pages/V4/Account/Login.cshtml | 3 +
.../V4/Account/ResendEmailConfirmation.cshtml | 26 +++++
.../Account/ResendEmailConfirmation.cshtml.cs | 103 ++++++++++++++++++
.../IdentitySample.DefaultUI/web.config | 8 +-
.../Identity.FunctionalTests/LoginTests.cs | 27 +++++
.../Pages/Account/Login.cs | 10 ++
.../Pages/Account/ResendEmailConfirmation.cs | 26 +++++
.../Identity.FunctionalTests/UserStories.cs | 10 ++
src/ProjectTemplates/test/Helpers/PageUrls.cs | 1 +
src/ProjectTemplates/test/MvcTemplateTest.cs | 1 +
.../test/RazorPagesTemplateTest.cs | 1 +
16 files changed, 376 insertions(+), 32 deletions(-)
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
create mode 100644 src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
diff --git a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
index 946e9c778cd5..416de9f01409 100644
--- a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
+++ b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
@@ -208,6 +208,22 @@ public InputModel() { }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
+ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
+ {
+ protected ResendEmailConfirmationModel() { }
+ [Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
+ public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public virtual void OnGet() { }
+ public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
+ public partial class InputModel
+ {
+ public InputModel() { }
+ [System.ComponentModel.DataAnnotations.EmailAddressAttribute]
+ [System.ComponentModel.DataAnnotations.RequiredAttribute]
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+ }
+ [Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
{
public ResetPasswordConfirmationModel() { }
@@ -658,6 +674,22 @@ public InputModel() { }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
+ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
+ {
+ protected ResendEmailConfirmationModel() { }
+ [Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
+ public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public virtual void OnGet() { }
+ public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
+ public partial class InputModel
+ {
+ public InputModel() { }
+ [System.ComponentModel.DataAnnotations.EmailAddressAttribute]
+ [System.ComponentModel.DataAnnotations.RequiredAttribute]
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+ }
+ [Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
{
public ResetPasswordConfirmationModel() { }
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
index a58edae8a87a..e17a218ba27b 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
@@ -42,7 +42,7 @@
Register as a new user
- Resend email confirmation
+ Resend email confirmation
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
index 74ec7bb1cd89..6006fb7f7f8f 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
@@ -169,34 +169,5 @@ public override async Task OnPostAsync(string returnUrl = null)
// If we got this far, something failed, redisplay form
return Page();
}
-
- public override async Task OnPostSendVerificationEmailAsync()
- {
- if (!ModelState.IsValid)
- {
- return Page();
- }
-
- var user = await _userManager.FindByEmailAsync(Input.Email);
- if (user == null)
- {
- ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
- }
-
- var userId = await _userManager.GetUserIdAsync(user);
- var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
- var callbackUrl = Url.Page(
- "/Account/ConfirmEmail",
- pageHandler: null,
- values: new { userId = userId, code = code },
- protocol: Request.Scheme);
- await _emailSender.SendEmailAsync(
- Input.Email,
- "Confirm your email",
- $"Please confirm your account by clicking here .");
-
- ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
- return Page();
- }
}
}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
new file mode 100644
index 000000000000..910891fb4682
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
@@ -0,0 +1,26 @@
+@page
+@model ResendEmailConfirmationModel
+@{
+ ViewData["Title"] = "Resend email confirmation";
+}
+
+@ViewData["Title"]
+Enter your email.
+
+
+
+@section Scripts {
+
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
new file mode 100644
index 000000000000..7ed2a11e5db0
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
@@ -0,0 +1,103 @@
+// 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.ComponentModel.DataAnnotations;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity.UI.Services;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
+{
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [AllowAnonymous]
+ [IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
+ public abstract class ResendEmailConfirmationModel : PageModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [BindProperty]
+ public InputModel Input { get; set; }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public class InputModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; }
+ }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual void OnGet() => throw new NotImplementedException();
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual Task OnPostAsync() => throw new NotImplementedException();
+ }
+
+ internal class ResendEmailConfirmationModel : ResendEmailConfirmationModel where TUser : class
+ {
+ private readonly UserManager _userManager;
+ private readonly IEmailSender _emailSender;
+
+ public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender)
+ {
+ _userManager = userManager;
+ _emailSender = emailSender;
+ }
+
+ public override void OnGet()
+ {
+ }
+
+ public override async Task OnPostAsync()
+ {
+ if (!ModelState.IsValid)
+ {
+ return Page();
+ }
+
+ var user = await _userManager.FindByEmailAsync(Input.Email);
+ if (user == null)
+ {
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+
+ var userId = await _userManager.GetUserIdAsync(user);
+ var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ var callbackUrl = Url.Page(
+ "/Account/ConfirmEmail",
+ pageHandler: null,
+ values: new { userId = userId, code = code },
+ protocol: Request.Scheme);
+ await _emailSender.SendEmailAsync(
+ Input.Email,
+ "Confirm your email",
+ $"Please confirm your account by clicking here .");
+
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+ }
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
index b98655e0f629..8f6f28a3a55c 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
@@ -41,6 +41,9 @@
Register as a new user
+
+ Resend email confirmation
+
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
new file mode 100644
index 000000000000..16d11e9d8ebd
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
@@ -0,0 +1,26 @@
+@page
+@model ResendEmailConfirmationModel
+@{
+ ViewData["Title"] = "Resend email confirmation";
+}
+
+@ViewData["Title"]
+Enter your email.
+
+
+
+@section Scripts {
+
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
new file mode 100644
index 000000000000..0635782a4202
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
@@ -0,0 +1,103 @@
+// 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.ComponentModel.DataAnnotations;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity.UI.Services;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
+{
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [AllowAnonymous]
+ [IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
+ public abstract class ResendEmailConfirmationModel : PageModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [BindProperty]
+ public InputModel Input { get; set; }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public class InputModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; }
+ }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual void OnGet() => throw new NotImplementedException();
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual Task OnPostAsync() => throw new NotImplementedException();
+ }
+
+ internal class ResendEmailConfirmationModel : ResendEmailConfirmationModel where TUser : class
+ {
+ private readonly UserManager _userManager;
+ private readonly IEmailSender _emailSender;
+
+ public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender)
+ {
+ _userManager = userManager;
+ _emailSender = emailSender;
+ }
+
+ public override void OnGet()
+ {
+ }
+
+ public override async Task OnPostAsync()
+ {
+ if (!ModelState.IsValid)
+ {
+ return Page();
+ }
+
+ var user = await _userManager.FindByEmailAsync(Input.Email);
+ if (user == null)
+ {
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+
+ var userId = await _userManager.GetUserIdAsync(user);
+ var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ var callbackUrl = Url.Page(
+ "/Account/ConfirmEmail",
+ pageHandler: null,
+ values: new { userId = userId, code = code },
+ protocol: Request.Scheme);
+ await _emailSender.SendEmailAsync(
+ Input.Email,
+ "Confirm your email",
+ $"Please confirm your account by clicking here .");
+
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+ }
+}
diff --git a/src/Identity/samples/IdentitySample.DefaultUI/web.config b/src/Identity/samples/IdentitySample.DefaultUI/web.config
index f7ac679334bc..6d25118e27e6 100644
--- a/src/Identity/samples/IdentitySample.DefaultUI/web.config
+++ b/src/Identity/samples/IdentitySample.DefaultUI/web.config
@@ -2,8 +2,12 @@
-
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
index 8a5125495ab2..bb7529a3a6c2 100644
--- a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
+++ b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
@@ -230,6 +230,33 @@ void ConfigureTestServices(IServiceCollection services) => services
await UserStories.LoginExistingUserAsync(newClient, userName, password);
}
+ [Fact]
+ public async Task CanResendConfirmingEmail()
+ {
+ // Arrange
+ var emailSender = new ContosoEmailSender();
+ void ConfigureTestServices(IServiceCollection services) => services
+ .SetupTestEmailSender(emailSender)
+ .SetupEmailRequired();
+
+ var server = ServerFactory.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices));
+
+ var client = server.CreateClient();
+ var newClient = server.CreateClient();
+
+ var userName = $"{Guid.NewGuid()}@example.com";
+ var password = $"!Test.Password1$";
+
+ var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
+
+ // Act & Assert
+ // Use a new client to simulate a new browser session.
+ await UserStories.ResendConfirmEmailAsync(server.CreateClient(), userName);
+ Assert.Equal(2, emailSender.SentEmails.Count);
+ var email = emailSender.SentEmails.Last();
+ await UserStories.ConfirmEmailAsync(email, newClient);
+ }
+
[Fact]
public async Task CanLogInAfterConfirmingEmail_WithGlobalAuthorizeFilter()
{
diff --git a/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs b/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
index 7db145000fa5..6960ac89645f 100644
--- a/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
+++ b/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
@@ -13,6 +13,7 @@ public class Login : DefaultUIPage
{
private readonly IHtmlFormElement _loginForm;
private readonly IHtmlAnchorElement _forgotPasswordLink;
+ private readonly IHtmlAnchorElement _reconfirmLink;
private readonly IHtmlFormElement _externalLoginForm;
private readonly IHtmlElement _contosoButton;
private readonly IHtmlElement _loginButton;
@@ -26,6 +27,7 @@ public Login(
_loginForm = HtmlAssert.HasForm("#account", login);
_loginButton = HtmlAssert.HasElement("#login-submit", login);
_forgotPasswordLink = HtmlAssert.HasLink("#forgot-password", login);
+ _reconfirmLink = HtmlAssert.HasLink("#resend-confirmation", login);
if (Context.ContosoLoginEnabled)
{
_externalLoginForm = HtmlAssert.HasForm("#external-account", login);
@@ -52,6 +54,14 @@ public async Task ClickForgotPasswordLinkAsync()
return new ForgotPassword(Client, forgotPassword, Context);
}
+ public async Task ClickReconfirmEmailLinkAsync()
+ {
+ var response = await Client.GetAsync(_reconfirmLink.Href);
+ var forgotPassword = await ResponseAssert.IsHtmlDocumentAsync(response);
+
+ return new ResendEmailConfirmation(Client, forgotPassword, Context);
+ }
+
public async Task LoginValidUserAsync(string userName, string password)
{
var loggedIn = await SendLoginForm(userName, password);
diff --git a/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
new file mode 100644
index 000000000000..d41cb142d8b0
--- /dev/null
+++ b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
@@ -0,0 +1,26 @@
+// 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
+{
+ public class ResendEmailConfirmation : DefaultUIPage
+ {
+ private readonly IHtmlFormElement _resendForm;
+
+ public ResendEmailConfirmation(HttpClient client, IHtmlDocument document, DefaultUIContext context) : base(client, document, context)
+ {
+ _resendForm = HtmlAssert.HasForm(document);
+ }
+
+ public Task ResendAsync(string email)
+ => Client.SendAsync(_resendForm, new Dictionary
+ {
+ ["Input_Email"] = email
+ });
+ }
+}
diff --git a/src/Identity/test/Identity.FunctionalTests/UserStories.cs b/src/Identity/test/Identity.FunctionalTests/UserStories.cs
index 2cc1cde82275..2610139fe053 100644
--- a/src/Identity/test/Identity.FunctionalTests/UserStories.cs
+++ b/src/Identity/test/Identity.FunctionalTests/UserStories.cs
@@ -194,6 +194,16 @@ internal static async Task ConfirmEmailAsync(IdentityEmail email,
.WithConfirmedEmail());
}
+ internal static async Task ResendConfirmEmailAsync(HttpClient client, string email)
+ {
+ var index = await Index.CreateAsync(client);
+ var login = await index.ClickLoginLinkAsync();
+ var reconfirm = await login.ClickReconfirmEmailLinkAsync();
+ var response = await reconfirm.ResendAsync(email);
+ ResponseAssert.IsOK(response);
+ Assert.Contains("Verification email sent.", await response.Content.ReadAsStringAsync());
+ }
+
internal static async Task ForgotPasswordAsync(HttpClient client, string userName)
{
var index = await Index.CreateAsync(client);
diff --git a/src/ProjectTemplates/test/Helpers/PageUrls.cs b/src/ProjectTemplates/test/Helpers/PageUrls.cs
index afb0783cbc4d..deb12fb16c65 100644
--- a/src/ProjectTemplates/test/Helpers/PageUrls.cs
+++ b/src/ProjectTemplates/test/Helpers/PageUrls.cs
@@ -12,6 +12,7 @@ public static class PageUrls
public const string LoginUrl = "/Identity/Account/Login";
public const string RegisterUrl = "/Identity/Account/Register";
public const string ForgotPassword = "/Identity/Account/ForgotPassword";
+ public const string ResendEmailConfirmation = "/Identity/Account/ResendEmailConfirmation";
public const string ExternalArticle = "https://go.microsoft.com/fwlink/?LinkID=532715";
}
}
diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs
index 95f6ce784add..cb83a5e06e3d 100644
--- a/src/ProjectTemplates/test/MvcTemplateTest.cs
+++ b/src/ProjectTemplates/test/MvcTemplateTest.cs
@@ -175,6 +175,7 @@ public async Task MvcTemplate_IndividualAuthImplAsync(bool useLocalDB)
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
+ PageUrls.ResendEmailConfirmation,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
diff --git a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
index 637d7179b571..bd9782b99b3c 100644
--- a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
+++ b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
@@ -174,6 +174,7 @@ public async Task RazorPagesTemplate_IndividualAuthImplAsync(bool useLocalDB)
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
+ PageUrls.ResendEmailConfirmation,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
From 79e68d6e40ad50afdc5db5991a413f67e14f0f39 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Wed, 18 Sep 2019 14:14:31 -0700
Subject: [PATCH 2/7] Fix resend email confirmation
Update src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
Co-Authored-By: campersau
Resend not reset
Update ref
Add new email confirmation link to templates test
Fix test
Revert website change
---
...osoft.AspNetCore.Identity.UI.netcoreapp.cs | 32 ++++++
.../Identity/Pages/V3/Account/Login.cshtml | 2 +-
.../Identity/Pages/V3/Account/Login.cshtml.cs | 29 -----
.../V3/Account/ResendEmailConfirmation.cshtml | 26 +++++
.../Account/ResendEmailConfirmation.cshtml.cs | 103 ++++++++++++++++++
.../Identity/Pages/V4/Account/Login.cshtml | 3 +
.../V4/Account/ResendEmailConfirmation.cshtml | 26 +++++
.../Account/ResendEmailConfirmation.cshtml.cs | 103 ++++++++++++++++++
.../IdentitySample.DefaultUI/web.config | 8 +-
.../Identity.FunctionalTests/LoginTests.cs | 27 +++++
.../Pages/Account/Login.cs | 10 ++
.../Pages/Account/ResendEmailConfirmation.cs | 26 +++++
.../Identity.FunctionalTests/UserStories.cs | 10 ++
src/ProjectTemplates/test/Helpers/PageUrls.cs | 1 +
src/ProjectTemplates/test/MvcTemplateTest.cs | 1 +
.../test/RazorPagesTemplateTest.cs | 1 +
16 files changed, 376 insertions(+), 32 deletions(-)
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
create mode 100644 src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
create mode 100644 src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
diff --git a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
index 946e9c778cd5..416de9f01409 100644
--- a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
+++ b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
@@ -208,6 +208,22 @@ public InputModel() { }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
+ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
+ {
+ protected ResendEmailConfirmationModel() { }
+ [Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
+ public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public virtual void OnGet() { }
+ public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
+ public partial class InputModel
+ {
+ public InputModel() { }
+ [System.ComponentModel.DataAnnotations.EmailAddressAttribute]
+ [System.ComponentModel.DataAnnotations.RequiredAttribute]
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+ }
+ [Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
{
public ResetPasswordConfirmationModel() { }
@@ -658,6 +674,22 @@ public InputModel() { }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
+ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
+ {
+ protected ResendEmailConfirmationModel() { }
+ [Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
+ public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public virtual void OnGet() { }
+ public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
+ public partial class InputModel
+ {
+ public InputModel() { }
+ [System.ComponentModel.DataAnnotations.EmailAddressAttribute]
+ [System.ComponentModel.DataAnnotations.RequiredAttribute]
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ }
+ }
+ [Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
public partial class ResetPasswordConfirmationModel : Microsoft.AspNetCore.Mvc.RazorPages.PageModel
{
public ResetPasswordConfirmationModel() { }
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
index 8ebfba2d1bdf..8f4559fb78e3 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml
@@ -42,7 +42,7 @@
Register as a new user
- Resend email confirmation
+ Resend email confirmation
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
index 74ec7bb1cd89..6006fb7f7f8f 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/Login.cshtml.cs
@@ -169,34 +169,5 @@ public override async Task OnPostAsync(string returnUrl = null)
// If we got this far, something failed, redisplay form
return Page();
}
-
- public override async Task OnPostSendVerificationEmailAsync()
- {
- if (!ModelState.IsValid)
- {
- return Page();
- }
-
- var user = await _userManager.FindByEmailAsync(Input.Email);
- if (user == null)
- {
- ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
- }
-
- var userId = await _userManager.GetUserIdAsync(user);
- var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
- var callbackUrl = Url.Page(
- "/Account/ConfirmEmail",
- pageHandler: null,
- values: new { userId = userId, code = code },
- protocol: Request.Scheme);
- await _emailSender.SendEmailAsync(
- Input.Email,
- "Confirm your email",
- $"Please confirm your account by clicking here .");
-
- ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
- return Page();
- }
}
}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
new file mode 100644
index 000000000000..910891fb4682
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml
@@ -0,0 +1,26 @@
+@page
+@model ResendEmailConfirmationModel
+@{
+ ViewData["Title"] = "Resend email confirmation";
+}
+
+@ViewData["Title"]
+Enter your email.
+
+
+
+@section Scripts {
+
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
new file mode 100644
index 000000000000..7ed2a11e5db0
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
@@ -0,0 +1,103 @@
+// 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.ComponentModel.DataAnnotations;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity.UI.Services;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
+{
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [AllowAnonymous]
+ [IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
+ public abstract class ResendEmailConfirmationModel : PageModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [BindProperty]
+ public InputModel Input { get; set; }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public class InputModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; }
+ }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual void OnGet() => throw new NotImplementedException();
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual Task OnPostAsync() => throw new NotImplementedException();
+ }
+
+ internal class ResendEmailConfirmationModel : ResendEmailConfirmationModel where TUser : class
+ {
+ private readonly UserManager _userManager;
+ private readonly IEmailSender _emailSender;
+
+ public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender)
+ {
+ _userManager = userManager;
+ _emailSender = emailSender;
+ }
+
+ public override void OnGet()
+ {
+ }
+
+ public override async Task OnPostAsync()
+ {
+ if (!ModelState.IsValid)
+ {
+ return Page();
+ }
+
+ var user = await _userManager.FindByEmailAsync(Input.Email);
+ if (user == null)
+ {
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+
+ var userId = await _userManager.GetUserIdAsync(user);
+ var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ var callbackUrl = Url.Page(
+ "/Account/ConfirmEmail",
+ pageHandler: null,
+ values: new { userId = userId, code = code },
+ protocol: Request.Scheme);
+ await _emailSender.SendEmailAsync(
+ Input.Email,
+ "Confirm your email",
+ $"Please confirm your account by clicking here .");
+
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+ }
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
index 7db27382777b..47b87509561e 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml
@@ -41,6 +41,9 @@
Register as a new user
+
+ Resend email confirmation
+
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
new file mode 100644
index 000000000000..16d11e9d8ebd
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml
@@ -0,0 +1,26 @@
+@page
+@model ResendEmailConfirmationModel
+@{
+ ViewData["Title"] = "Resend email confirmation";
+}
+
+@ViewData["Title"]
+Enter your email.
+
+
+
+@section Scripts {
+
+}
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
new file mode 100644
index 000000000000..0635782a4202
--- /dev/null
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
@@ -0,0 +1,103 @@
+// 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.ComponentModel.DataAnnotations;
+using System.Text.Encodings.Web;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Identity.UI.Services;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.RazorPages;
+
+namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
+{
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [AllowAnonymous]
+ [IdentityDefaultUI(typeof(ResendEmailConfirmationModel<>))]
+ public abstract class ResendEmailConfirmationModel : PageModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [BindProperty]
+ public InputModel Input { get; set; }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public class InputModel
+ {
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ [Required]
+ [EmailAddress]
+ public string Email { get; set; }
+ }
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual void OnGet() => throw new NotImplementedException();
+
+ ///
+ /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
+ /// directly from your code. This API may change or be removed in future releases.
+ ///
+ public virtual Task OnPostAsync() => throw new NotImplementedException();
+ }
+
+ internal class ResendEmailConfirmationModel : ResendEmailConfirmationModel where TUser : class
+ {
+ private readonly UserManager _userManager;
+ private readonly IEmailSender _emailSender;
+
+ public ResendEmailConfirmationModel(UserManager userManager, IEmailSender emailSender)
+ {
+ _userManager = userManager;
+ _emailSender = emailSender;
+ }
+
+ public override void OnGet()
+ {
+ }
+
+ public override async Task OnPostAsync()
+ {
+ if (!ModelState.IsValid)
+ {
+ return Page();
+ }
+
+ var user = await _userManager.FindByEmailAsync(Input.Email);
+ if (user == null)
+ {
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+
+ var userId = await _userManager.GetUserIdAsync(user);
+ var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ var callbackUrl = Url.Page(
+ "/Account/ConfirmEmail",
+ pageHandler: null,
+ values: new { userId = userId, code = code },
+ protocol: Request.Scheme);
+ await _emailSender.SendEmailAsync(
+ Input.Email,
+ "Confirm your email",
+ $"Please confirm your account by clicking here .");
+
+ ModelState.AddModelError(string.Empty, "Verification email sent. Please check your email.");
+ return Page();
+ }
+ }
+}
diff --git a/src/Identity/samples/IdentitySample.DefaultUI/web.config b/src/Identity/samples/IdentitySample.DefaultUI/web.config
index f7ac679334bc..6d25118e27e6 100644
--- a/src/Identity/samples/IdentitySample.DefaultUI/web.config
+++ b/src/Identity/samples/IdentitySample.DefaultUI/web.config
@@ -2,8 +2,12 @@
-
+
-
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
index 8a5125495ab2..bb7529a3a6c2 100644
--- a/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
+++ b/src/Identity/test/Identity.FunctionalTests/LoginTests.cs
@@ -230,6 +230,33 @@ void ConfigureTestServices(IServiceCollection services) => services
await UserStories.LoginExistingUserAsync(newClient, userName, password);
}
+ [Fact]
+ public async Task CanResendConfirmingEmail()
+ {
+ // Arrange
+ var emailSender = new ContosoEmailSender();
+ void ConfigureTestServices(IServiceCollection services) => services
+ .SetupTestEmailSender(emailSender)
+ .SetupEmailRequired();
+
+ var server = ServerFactory.WithWebHostBuilder(whb => whb.ConfigureServices(ConfigureTestServices));
+
+ var client = server.CreateClient();
+ var newClient = server.CreateClient();
+
+ var userName = $"{Guid.NewGuid()}@example.com";
+ var password = $"!Test.Password1$";
+
+ var loggedIn = await UserStories.RegisterNewUserAsync(client, userName, password);
+
+ // Act & Assert
+ // Use a new client to simulate a new browser session.
+ await UserStories.ResendConfirmEmailAsync(server.CreateClient(), userName);
+ Assert.Equal(2, emailSender.SentEmails.Count);
+ var email = emailSender.SentEmails.Last();
+ await UserStories.ConfirmEmailAsync(email, newClient);
+ }
+
[Fact]
public async Task CanLogInAfterConfirmingEmail_WithGlobalAuthorizeFilter()
{
diff --git a/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs b/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
index 7db145000fa5..6960ac89645f 100644
--- a/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
+++ b/src/Identity/test/Identity.FunctionalTests/Pages/Account/Login.cs
@@ -13,6 +13,7 @@ public class Login : DefaultUIPage
{
private readonly IHtmlFormElement _loginForm;
private readonly IHtmlAnchorElement _forgotPasswordLink;
+ private readonly IHtmlAnchorElement _reconfirmLink;
private readonly IHtmlFormElement _externalLoginForm;
private readonly IHtmlElement _contosoButton;
private readonly IHtmlElement _loginButton;
@@ -26,6 +27,7 @@ public Login(
_loginForm = HtmlAssert.HasForm("#account", login);
_loginButton = HtmlAssert.HasElement("#login-submit", login);
_forgotPasswordLink = HtmlAssert.HasLink("#forgot-password", login);
+ _reconfirmLink = HtmlAssert.HasLink("#resend-confirmation", login);
if (Context.ContosoLoginEnabled)
{
_externalLoginForm = HtmlAssert.HasForm("#external-account", login);
@@ -52,6 +54,14 @@ public async Task ClickForgotPasswordLinkAsync()
return new ForgotPassword(Client, forgotPassword, Context);
}
+ public async Task ClickReconfirmEmailLinkAsync()
+ {
+ var response = await Client.GetAsync(_reconfirmLink.Href);
+ var forgotPassword = await ResponseAssert.IsHtmlDocumentAsync(response);
+
+ return new ResendEmailConfirmation(Client, forgotPassword, Context);
+ }
+
public async Task LoginValidUserAsync(string userName, string password)
{
var loggedIn = await SendLoginForm(userName, password);
diff --git a/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
new file mode 100644
index 000000000000..d41cb142d8b0
--- /dev/null
+++ b/src/Identity/test/Identity.FunctionalTests/Pages/Account/ResendEmailConfirmation.cs
@@ -0,0 +1,26 @@
+// 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
+{
+ public class ResendEmailConfirmation : DefaultUIPage
+ {
+ private readonly IHtmlFormElement _resendForm;
+
+ public ResendEmailConfirmation(HttpClient client, IHtmlDocument document, DefaultUIContext context) : base(client, document, context)
+ {
+ _resendForm = HtmlAssert.HasForm(document);
+ }
+
+ public Task ResendAsync(string email)
+ => Client.SendAsync(_resendForm, new Dictionary
+ {
+ ["Input_Email"] = email
+ });
+ }
+}
diff --git a/src/Identity/test/Identity.FunctionalTests/UserStories.cs b/src/Identity/test/Identity.FunctionalTests/UserStories.cs
index 2cc1cde82275..2610139fe053 100644
--- a/src/Identity/test/Identity.FunctionalTests/UserStories.cs
+++ b/src/Identity/test/Identity.FunctionalTests/UserStories.cs
@@ -194,6 +194,16 @@ internal static async Task ConfirmEmailAsync(IdentityEmail email,
.WithConfirmedEmail());
}
+ internal static async Task ResendConfirmEmailAsync(HttpClient client, string email)
+ {
+ var index = await Index.CreateAsync(client);
+ var login = await index.ClickLoginLinkAsync();
+ var reconfirm = await login.ClickReconfirmEmailLinkAsync();
+ var response = await reconfirm.ResendAsync(email);
+ ResponseAssert.IsOK(response);
+ Assert.Contains("Verification email sent.", await response.Content.ReadAsStringAsync());
+ }
+
internal static async Task ForgotPasswordAsync(HttpClient client, string userName)
{
var index = await Index.CreateAsync(client);
diff --git a/src/ProjectTemplates/test/Helpers/PageUrls.cs b/src/ProjectTemplates/test/Helpers/PageUrls.cs
index afb0783cbc4d..deb12fb16c65 100644
--- a/src/ProjectTemplates/test/Helpers/PageUrls.cs
+++ b/src/ProjectTemplates/test/Helpers/PageUrls.cs
@@ -12,6 +12,7 @@ public static class PageUrls
public const string LoginUrl = "/Identity/Account/Login";
public const string RegisterUrl = "/Identity/Account/Register";
public const string ForgotPassword = "/Identity/Account/ForgotPassword";
+ public const string ResendEmailConfirmation = "/Identity/Account/ResendEmailConfirmation";
public const string ExternalArticle = "https://go.microsoft.com/fwlink/?LinkID=532715";
}
}
diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs
index 9fc1ab4a043f..fea6d42783cd 100644
--- a/src/ProjectTemplates/test/MvcTemplateTest.cs
+++ b/src/ProjectTemplates/test/MvcTemplateTest.cs
@@ -183,6 +183,7 @@ public async Task MvcTemplate_IndividualAuth(bool useLocalDB)
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
+ PageUrls.ResendEmailConfirmation,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
diff --git a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
index 7cd41d56b26e..ad7c967d48d6 100644
--- a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
+++ b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs
@@ -172,6 +172,7 @@ public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB)
PageUrls.PrivacyUrl,
PageUrls.ForgotPassword,
PageUrls.RegisterUrl,
+ PageUrls.ResendEmailConfirmation,
PageUrls.ExternalArticle,
PageUrls.PrivacyUrl }
},
From 97b7b01116b30179d5bbf40bf0fff42ad70062d3 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Wed, 2 Oct 2019 22:36:43 -0700
Subject: [PATCH 3/7] Encode the code
---
.../Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
index 7ed2a11e5db0..54e6afb839d2 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
@@ -9,6 +9,7 @@
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.WebUtilities;
namespace Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal
{
@@ -86,6 +87,7 @@ public override async Task OnPostAsync()
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
From 79dbae2000925827705b2631d49c8ff0b469ab3d Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Wed, 2 Oct 2019 22:37:18 -0700
Subject: [PATCH 4/7] Fix V4 encoding
---
.../Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
index 0635782a4202..7e9c9b69b9f4 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
@@ -9,6 +9,7 @@
using Microsoft.AspNetCore.Identity.UI.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
+using Microsoft.AspNetCore.WebUtilities;
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal
{
@@ -86,6 +87,7 @@ public override async Task OnPostAsync()
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
+ code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
From 4364575deb2dd1b8d3468e31444f61139b877793 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Mon, 4 Nov 2019 12:11:36 -0800
Subject: [PATCH 5/7] Fix using
---
.../Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs | 1 +
.../Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
index 54e6afb839d2..d1d2ef784698 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V3/Account/ResendEmailConfirmation.cshtml.cs
@@ -3,6 +3,7 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
diff --git a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
index 7e9c9b69b9f4..559ce47bc525 100644
--- a/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
+++ b/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/ResendEmailConfirmation.cshtml.cs
@@ -3,6 +3,7 @@
using System;
using System.ComponentModel.DataAnnotations;
+using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
From be8f8608c5164b9139a651d3fa4ba3b76e1ddbd6 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Mon, 4 Nov 2019 13:09:10 -0800
Subject: [PATCH 6/7] Update Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
---
.../UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
index 39a607ce5555..0fc1197ed66a 100644
--- a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
+++ b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
@@ -211,7 +211,7 @@ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCor
{
protected ResendEmailConfirmationModel() { }
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
- public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual void OnGet() { }
public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
public partial class InputModel
@@ -219,7 +219,7 @@ public partial class InputModel
public InputModel() { }
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
[System.ComponentModel.DataAnnotations.RequiredAttribute]
- public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
@@ -677,7 +677,7 @@ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCor
{
protected ResendEmailConfirmationModel() { }
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
- public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual void OnGet() { }
public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
public partial class InputModel
@@ -685,7 +685,7 @@ public partial class InputModel
public InputModel() { }
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
[System.ComponentModel.DataAnnotations.RequiredAttribute]
- public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
From 17593980d08d8e251a8a69ebee2ad5f8314542d7 Mon Sep 17 00:00:00 2001
From: Hao Kung
Date: Mon, 4 Nov 2019 14:54:36 -0800
Subject: [PATCH 7/7] Fix ref
---
.../UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
index 0fc1197ed66a..4f25f3b9fab4 100644
--- a/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
+++ b/src/Identity/UI/ref/Microsoft.AspNetCore.Identity.UI.netcoreapp.cs
@@ -211,7 +211,7 @@ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCor
{
protected ResendEmailConfirmationModel() { }
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
- public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public Microsoft.AspNetCore.Identity.UI.V3.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public virtual void OnGet() { }
public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
public partial class InputModel
@@ -219,7 +219,7 @@ public partial class InputModel
public InputModel() { }
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
[System.ComponentModel.DataAnnotations.RequiredAttribute]
- public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]
@@ -677,7 +677,7 @@ public abstract partial class ResendEmailConfirmationModel : Microsoft.AspNetCor
{
protected ResendEmailConfirmationModel() { }
[Microsoft.AspNetCore.Mvc.BindPropertyAttribute]
- public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Internal.ResendEmailConfirmationModel.InputModel Input { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public virtual void OnGet() { }
public virtual System.Threading.Tasks.Task OnPostAsync() { throw null; }
public partial class InputModel
@@ -685,7 +685,7 @@ public partial class InputModel
public InputModel() { }
[System.ComponentModel.DataAnnotations.EmailAddressAttribute]
[System.ComponentModel.DataAnnotations.RequiredAttribute]
- public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
+ public string Email { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
}
[Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute]