[Fixes #1690] AddDefaultUI() throws when using IdentityUser<T>

This commit is contained in:
Kiran Challa 2018-03-27 15:52:06 -07:00
parent c7711c2515
commit 971f727ea2
27 changed files with 178 additions and 66 deletions

View File

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26730.12
VisualStudioVersion = 15.0.27130.2027
MinimumVisualStudioVersion = 15.0.26730.03
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0F647068-6602-4E24-B1DC-8ED91481A50A}"
ProjectSection(SolutionItems) = preProject

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnGetAsync(string userId, string code) => throw new NotImplementedException();
}
internal class ConfirmEmailModel<TUser> : ConfirmEmailModel where TUser : IdentityUser
internal class ConfirmEmailModel<TUser> : ConfirmEmailModel where TUser : class
{
private readonly UserManager<TUser> _userManager;

View File

@ -40,19 +40,22 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null) => throw new NotImplementedException();
}
internal class ExternalLoginModel<TUser> : ExternalLoginModel where TUser : IdentityUser, new()
internal class ExternalLoginModel<TUser> : ExternalLoginModel where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly UserManager<TUser> _userManager;
private readonly IUserFactory<TUser> _userFactory;
private readonly ILogger<ExternalLoginModel> _logger;
public ExternalLoginModel(
SignInManager<TUser> signInManager,
UserManager<TUser> userManager,
IUserFactory<TUser> userFactory,
ILogger<ExternalLoginModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;
_userFactory = userFactory;
_logger = logger;
}
@ -124,7 +127,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
if (ModelState.IsValid)
{
var user = new TUser { UserName = Input.Email, Email = Input.Email };
var user = _userFactory.CreateUser(email: Input.Email, userName: Input.Email);
var result = await _userManager.CreateAsync(user);
if (result.Succeeded)
{

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class ForgotPasswordModel<TUser> : ForgotPasswordModel where TUser : IdentityUser
internal class ForgotPasswordModel<TUser> : ForgotPasswordModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly IEmailSender _emailSender;

View File

@ -47,7 +47,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
}
internal class LoginModel<TUser> : LoginModel where TUser : IdentityUser
internal class LoginModel<TUser> : LoginModel where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly ILogger<LoginModel> _logger;

View File

@ -39,14 +39,19 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync(bool rememberMe, string returnUrl = null) => throw new NotImplementedException();
}
internal class LoginWith2faModel<TUser> : LoginWith2faModel where TUser : IdentityUser
internal class LoginWith2faModel<TUser> : LoginWith2faModel where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly UserManager<TUser> _userManager;
private readonly ILogger<LoginWith2faModel> _logger;
public LoginWith2faModel(SignInManager<TUser> signInManager, ILogger<LoginWith2faModel> logger)
public LoginWith2faModel(
SignInManager<TUser> signInManager,
UserManager<TUser> userManager,
ILogger<LoginWith2faModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;
_logger = logger;
}
@ -85,19 +90,21 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine);
var userId = await _userManager.GetUserIdAsync(user);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id);
_logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", userId);
return LocalRedirect(returnUrl);
}
else if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
_logger.LogWarning("User with ID '{UserId}' account locked out.", userId);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id);
_logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId);
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return Page();
}

View File

@ -34,14 +34,19 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
}
internal class LoginWithRecoveryCodeModel<TUser> : LoginWithRecoveryCodeModel where TUser : IdentityUser
internal class LoginWithRecoveryCodeModel<TUser> : LoginWithRecoveryCodeModel where TUser: class
{
private readonly SignInManager<TUser> _signInManager;
private readonly UserManager<TUser> _userManager;
private readonly ILogger<LoginWithRecoveryCodeModel> _logger;
public LoginWithRecoveryCodeModel(SignInManager<TUser> signInManager, ILogger<LoginWithRecoveryCodeModel> logger)
public LoginWithRecoveryCodeModel(
SignInManager<TUser> signInManager,
UserManager<TUser> userManager,
ILogger<LoginWithRecoveryCodeModel> logger)
{
_signInManager = signInManager;
_userManager = userManager;
_logger = logger;
}
@ -76,19 +81,21 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
var userId = await _userManager.GetUserIdAsync(user);
if (result.Succeeded)
{
_logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", user.Id);
_logger.LogInformation("User with ID '{UserId}' logged in with a recovery code.", userId);
return LocalRedirect(returnUrl ?? Url.Content("~/"));
}
if (result.IsLockedOut)
{
_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
_logger.LogWarning("User with ID '{UserId}' account locked out.", userId);
return RedirectToPage("./Lockout");
}
else
{
_logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", user.Id);
_logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId);
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
return Page();
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPost(string returnUrl = null) => throw new NotImplementedException();
}
internal class LogoutModel<TUser> : LogoutModel where TUser : IdentityUser
internal class LogoutModel<TUser> : LogoutModel where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly ILogger<LogoutModel> _logger;

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class ChangePasswordModel<TUser> : ChangePasswordModel where TUser : IdentityUser
internal class ChangePasswordModel<TUser> : ChangePasswordModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class DeletePersonalDataModel<TUser> : DeletePersonalDataModel where TUser : IdentityUser
internal class DeletePersonalDataModel<TUser> : DeletePersonalDataModel where TUser: class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;
@ -77,14 +77,15 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
}
var result = await _userManager.DeleteAsync(user);
var userId = await _userManager.GetUserIdAsync(user);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{user.Id}'.");
throw new InvalidOperationException($"Unexpected error occurred deleteing user with ID '{userId}'.");
}
await _signInManager.SignOutAsync();
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", _userManager.GetUserId(User));
_logger.LogInformation("User with ID '{UserId}' deleted themselves.", userId);
return Redirect("~/");
}

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class Disable2faModel<TUser> : Disable2faModel where TUser : IdentityUser
internal class Disable2faModel<TUser> : Disable2faModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly ILogger<Disable2faModel> _logger;

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class DownloadPersonalDataModel<TUser> : DownloadPersonalDataModel where TUser : IdentityUser
internal class DownloadPersonalDataModel<TUser> : DownloadPersonalDataModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly ILogger<DownloadPersonalDataModel> _logger;

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class EnableAuthenticatorModel<TUser> : EnableAuthenticatorModel where TUser : IdentityUser
internal class EnableAuthenticatorModel<TUser> : EnableAuthenticatorModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly ILogger<EnableAuthenticatorModel> _logger;
@ -102,7 +102,8 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
}
await _userManager.SetTwoFactorEnabledAsync(user, true);
_logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", user.Id);
var userId = await _userManager.GetUserIdAsync(user);
_logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId);
StatusMessage = "Your authenticator app has been verified.";
@ -129,7 +130,9 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
}
SharedKey = FormatKey(unformattedKey);
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
var email = await _userManager.GetEmailAsync(user);
AuthenticatorUri = GenerateQrCodeUri(email, unformattedKey);
}
private string FormatKey(string unformattedKey)

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
@ -32,17 +33,20 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnGetLinkLoginCallbackAsync() => throw new NotImplementedException();
}
internal class ExternalLoginsModel<TUser> : ExternalLoginsModel where TUser : IdentityUser
internal class ExternalLoginsModel<TUser> : ExternalLoginsModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;
private readonly IUserStore<TUser> _userStore;
public ExternalLoginsModel(
UserManager<TUser> userManager,
SignInManager<TUser> signInManager)
SignInManager<TUser> signInManager,
IUserStore<TUser> userStore)
{
_userManager = userManager;
_signInManager = signInManager;
_userStore = userStore;
}
public override async Task<IActionResult> OnGetAsync()
@ -57,7 +61,14 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
OtherLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync())
.Where(auth => CurrentLogins.All(ul => auth.Name != ul.LoginProvider))
.ToList();
ShowRemoveButton = user.PasswordHash != null || CurrentLogins.Count > 1;
string passwordHash = null;
if (_userStore is IUserPasswordStore<TUser> userPasswordStore)
{
passwordHash = await userPasswordStore.GetPasswordHashAsync(user, HttpContext.RequestAborted);
}
ShowRemoveButton = passwordHash != null || CurrentLogins.Count > 1;
return Page();
}
@ -72,7 +83,8 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
var result = await _userManager.RemoveLoginAsync(user, loginProvider, providerKey);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{user.Id}'.");
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred removing external login for user with ID '{userId}'.");
}
await _signInManager.RefreshSignInAsync(user);
@ -99,16 +111,17 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var info = await _signInManager.GetExternalLoginInfoAsync(await _userManager.GetUserIdAsync(user));
var userId = await _userManager.GetUserIdAsync(user);
var info = await _signInManager.GetExternalLoginInfoAsync(userId);
if (info == null)
{
throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{user.Id}'.");
throw new InvalidOperationException($"Unexpected error occurred loading external login info for user with ID '{userId}'.");
}
var result = await _userManager.AddLoginAsync(user, info);
if (!result.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred adding external login for user with ID '{user.Id}'.");
throw new InvalidOperationException($"Unexpected error occurred adding external login for user with ID '{userId}'.");
}
// Clear the existing external cookie to ensure a clean login process

View File

@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class GenerateRecoveryCodesModel<TUser> : GenerateRecoveryCodesModel where TUser : IdentityUser
internal class GenerateRecoveryCodesModel<TUser> : GenerateRecoveryCodesModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly ILogger<GenerateRecoveryCodesModel> _logger;
@ -45,9 +45,11 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!user.TwoFactorEnabled)
var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
if (!isTwoFactorEnabled)
{
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{user.Id}' because they do not have 2FA enabled.");
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' because they do not have 2FA enabled.");
}
return Page();
@ -61,15 +63,17 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (!user.TwoFactorEnabled)
var isTwoFactorEnabled = await _userManager.GetTwoFactorEnabledAsync(user);
var userId = await _userManager.GetUserIdAsync(user);
if (!isTwoFactorEnabled)
{
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
throw new InvalidOperationException($"Cannot generate recovery codes for user with ID '{userId}' as they do not have 2FA enabled.");
}
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
RecoveryCodes = recoveryCodes.ToArray();
_logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", user.Id);
_logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId);
StatusMessage = "You have generated new recovery codes.";
return RedirectToPage("./ShowRecoveryCodes");
}

View File

@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostSendVerificationEmailAsync() => throw new NotImplementedException();
}
internal class IndexModel<TUser> : IndexModel where TUser : IdentityUser
internal class IndexModel<TUser> : IndexModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;
@ -65,13 +65,16 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
{
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var userName = await _userManager.GetUserNameAsync(user);
var email = await _userManager.GetEmailAsync(user);
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
Username = user.UserName;
Username = userName;
Input = new InputModel
{
Email = user.Email,
PhoneNumber = user.PhoneNumber
Email = email,
PhoneNumber = phoneNumber
};
IsEmailConfirmed = await _userManager.IsEmailConfirmedAsync(user);
@ -92,21 +95,25 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
if (Input.Email != user.Email)
var email = await _userManager.GetEmailAsync(user);
if (Input.Email != email)
{
var setEmailResult = await _userManager.SetEmailAsync(user, Input.Email);
if (!setEmailResult.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{user.Id}'.");
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting email for user with ID '{userId}'.");
}
}
if (Input.PhoneNumber != user.PhoneNumber)
var phoneNumber = await _userManager.GetPhoneNumberAsync(user);
if (Input.PhoneNumber != phoneNumber)
{
var setPhoneResult = await _userManager.SetPhoneNumberAsync(user, Input.PhoneNumber);
if (!setPhoneResult.Succeeded)
{
throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{user.Id}'.");
var userId = await _userManager.GetUserIdAsync(user);
throw new InvalidOperationException($"Unexpected error occurred setting phone number for user with ID '{userId}'.");
}
}
@ -128,14 +135,17 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
return NotFound($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
}
var userId = await _userManager.GetUserIdAsync(user);
var email = await _userManager.GetEmailAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
values: new { userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(
user.Email,
email,
"Confirm your email",
$"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");

View File

@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnGet() => throw new NotImplementedException();
}
internal class PersonalDataModel<TUser> : PersonalDataModel where TUser : IdentityUser
internal class PersonalDataModel<TUser> : PersonalDataModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly ILogger<PersonalDataModel> _logger;

View File

@ -20,7 +20,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class ResetAuthenticatorModel<TUser> : ResetAuthenticatorModel where TUser : IdentityUser
internal class ResetAuthenticatorModel<TUser> : ResetAuthenticatorModel where TUser : class
{
UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;
@ -57,7 +57,8 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
await _userManager.SetTwoFactorEnabledAsync(user, false);
await _userManager.ResetAuthenticatorKeyAsync(user);
_logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", user.Id);
var userId = await _userManager.GetUserIdAsync(user);
_logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", userId);
await _signInManager.RefreshSignInAsync(user);
StatusMessage = "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.";

View File

@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class SetPasswordModel<TUser> : SetPasswordModel where TUser : IdentityUser
internal class SetPasswordModel<TUser> : SetPasswordModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;

View File

@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Manage.Internal
}
internal class TwoFactorAuthenticationModel<TUser> : TwoFactorAuthenticationModel where TUser : IdentityUser
internal class TwoFactorAuthenticationModel<TUser> : TwoFactorAuthenticationModel where TUser : class
{
private readonly UserManager<TUser> _userManager;
private readonly SignInManager<TUser> _signInManager;

View File

@ -46,9 +46,10 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync(string returnUrl = null) => throw new NotImplementedException();
}
internal class RegisterModel<TUser> : RegisterModel where TUser : IdentityUser, new()
internal class RegisterModel<TUser> : RegisterModel where TUser : class
{
private readonly SignInManager<TUser> _signInManager;
private readonly IUserFactory<TUser> _userFactory;
private readonly UserManager<TUser> _userManager;
private readonly ILogger<LoginModel> _logger;
private readonly IEmailSender _emailSender;
@ -56,11 +57,13 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public RegisterModel(
UserManager<TUser> userManager,
SignInManager<TUser> signInManager,
IUserFactory<TUser> userFactory,
ILogger<LoginModel> logger,
IEmailSender emailSender)
{
_userManager = userManager;
_signInManager = signInManager;
_userFactory = userFactory;
_logger = logger;
_emailSender = emailSender;
}
@ -75,17 +78,18 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var user = new TUser { UserName = Input.Email, Email = Input.Email };
var user = _userFactory.CreateUser(email: Input.Email, userName: Input.Email);
var result = await _userManager.CreateAsync(user, Input.Password);
if (result.Succeeded)
{
_logger.LogInformation("User created a new account with password.");
var userId = await _userManager.GetUserIdAsync(user);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = user.Id, code = code },
values: new { userId = userId, code = code },
protocol: Request.Scheme);
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",

View File

@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Identity.UI.Pages.Account.Internal
public virtual Task<IActionResult> OnPostAsync() => throw new NotImplementedException();
}
internal class ResetPasswordModel<TUser> : ResetPasswordModel where TUser : IdentityUser
internal class ResetPasswordModel<TUser> : ResetPasswordModel where TUser : class
{
private readonly UserManager<TUser> _userManager;

20
src/UI/IUserFactory.cs Normal file
View File

@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.AspNetCore.Identity.UI
{
/// <summary>
/// Provides an abstraction for instantiating the given user type.
/// </summary>
/// <typeparam name="TUser">The type of user.</typeparam>
public interface IUserFactory<TUser> where TUser : class
{
/// <summary>
/// Creates an instance of a user and assigns the provided values.
/// </summary>
/// <param name="email">Email address</param>
/// <param name="userName">User name</param>
/// <returns>Created user</returns>
TUser CreateUser(string email, string userName);
}
}

View File

@ -1,6 +1,7 @@
// 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.Linq;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Identity.UI.Services;
@ -36,6 +37,13 @@ namespace Microsoft.AspNetCore.Identity
.MakeGenericType(builder.UserType));
builder.Services.TryAddTransient<IEmailSender, EmailSender>();
if (TryGetIdentityUserType(builder.UserType, out var primaryKeyType))
{
var userFactoryType = typeof(IUserFactory<>).MakeGenericType(builder.UserType);
var defaultUserFactoryType = typeof(UserFactory<,>).MakeGenericType(builder.UserType, primaryKeyType);
builder.Services.TryAddSingleton(userFactoryType, defaultUserFactoryType);
}
return builder;
}
@ -63,5 +71,24 @@ namespace Microsoft.AspNetCore.Identity
}
});
}
private static bool TryGetIdentityUserType(Type userType, out Type primaryKeyType)
{
primaryKeyType = null;
var baseType = userType.BaseType;
while (baseType != null)
{
if (baseType.IsGenericType &&
baseType.GetGenericTypeDefinition() == typeof(IdentityUser<>))
{
primaryKeyType = baseType.GetGenericArguments()[0];
return true;
}
baseType = baseType.BaseType;
}
return false;
}
}
}

View File

@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Identity.UI
internal class IdentityDefaultUIConfigureOptions<TUser> :
IPostConfigureOptions<RazorPagesOptions>,
IPostConfigureOptions<StaticFileOptions>,
IPostConfigureOptions<CookieAuthenticationOptions> where TUser : IdentityUser
IPostConfigureOptions<CookieAuthenticationOptions> where TUser : class
{
private const string IdentityUIDefaultAreaName = "Identity";

View File

@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Mvc.ApplicationModels;
namespace Microsoft.AspNetCore.Identity.UI
{
internal class IdentityPageModelConvention<TUser> : IPageApplicationModelConvention where TUser : IdentityUser
internal class IdentityPageModelConvention<TUser> : IPageApplicationModelConvention where TUser : class
{
public void Apply(PageApplicationModel model)
{
@ -24,7 +24,7 @@ namespace Microsoft.AspNetCore.Identity.UI
private void ValidateTemplate(Type template)
{
if(template.IsAbstract || !template.IsGenericTypeDefinition)
if (template.IsAbstract || !template.IsGenericTypeDefinition)
{
throw new InvalidOperationException("Implementation type can't be abstract or non generic.");
}
@ -33,11 +33,6 @@ namespace Microsoft.AspNetCore.Identity.UI
{
throw new InvalidOperationException("Implementation type contains wrong generic arity.");
}
var argument = genericArguments[0];
if (!typeof(IdentityUser).IsAssignableFrom(typeof(TUser)))
{
throw new InvalidOperationException("Generic implementation type is not compatible.");
};
}
}
}

17
src/UI/UserFactory.cs Normal file
View File

@ -0,0 +1,17 @@
// 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;
namespace Microsoft.AspNetCore.Identity.UI
{
internal class UserFactory<TUser, TKey> : IUserFactory<TUser>
where TUser : IdentityUser<TKey>, new()
where TKey : IEquatable<TKey>
{
public TUser CreateUser(string email, string userName)
{
return new TUser() { Email = email, UserName = userName };
}
}
}