+
+
+
+
diff --git a/RobotApp.Client/_Imports.razor b/RobotApp.Client/_Imports.razor
index 11e67c1..f3b923f 100644
--- a/RobotApp.Client/_Imports.razor
+++ b/RobotApp.Client/_Imports.razor
@@ -8,3 +8,4 @@
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using RobotApp.Client
+@using MudBlazor
diff --git a/RobotApp.Client/wwwroot/images/Image-not-found.png b/RobotApp.Client/wwwroot/images/Image-not-found.png
new file mode 100644
index 0000000..9804d3c
Binary files /dev/null and b/RobotApp.Client/wwwroot/images/Image-not-found.png differ
diff --git a/RobotApp.Client/wwwroot/images/logoDark.svg b/RobotApp.Client/wwwroot/images/logoDark.svg
new file mode 100644
index 0000000..b0b66b4
--- /dev/null
+++ b/RobotApp.Client/wwwroot/images/logoDark.svg
@@ -0,0 +1,10 @@
+
diff --git a/RobotApp.Client/wwwroot/images/logoLight.svg b/RobotApp.Client/wwwroot/images/logoLight.svg
new file mode 100644
index 0000000..cacd11d
--- /dev/null
+++ b/RobotApp.Client/wwwroot/images/logoLight.svg
@@ -0,0 +1,48 @@
+
diff --git a/RobotApp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs b/RobotApp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs
index 3dbdb5b..7b9de05 100644
--- a/RobotApp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs
+++ b/RobotApp/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs
@@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using RobotApp.Components.Account.Pages;
-using RobotApp.Components.Account.Pages.Manage;
+using RobotApp.Components.Account.Shared;
using RobotApp.Data;
namespace Microsoft.AspNetCore.Routing
@@ -21,92 +21,15 @@ namespace Microsoft.AspNetCore.Routing
var accountGroup = endpoints.MapGroup("/Account");
- accountGroup.MapPost("/PerformExternalLogin", (
- HttpContext context,
- [FromServices] SignInManager signInManager,
- [FromForm] string provider,
- [FromForm] string returnUrl) =>
- {
- IEnumerable> query = [
- new("ReturnUrl", returnUrl),
- new("Action", ExternalLogin.LoginCallbackAction)];
-
- var redirectUrl = UriHelper.BuildRelative(
- context.Request.PathBase,
- "/Account/ExternalLogin",
- QueryString.Create(query));
-
- var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
- return TypedResults.Challenge(properties, [provider]);
- });
-
accountGroup.MapPost("/Logout", async (
ClaimsPrincipal user,
- [FromServices] SignInManager signInManager,
+ SignInManager signInManager,
[FromForm] string returnUrl) =>
{
await signInManager.SignOutAsync();
return TypedResults.LocalRedirect($"~/{returnUrl}");
});
- var manageGroup = accountGroup.MapGroup("/Manage").RequireAuthorization();
-
- manageGroup.MapPost("/LinkExternalLogin", async (
- HttpContext context,
- [FromServices] SignInManager signInManager,
- [FromForm] string provider) =>
- {
- // Clear the existing external cookie to ensure a clean login process
- await context.SignOutAsync(IdentityConstants.ExternalScheme);
-
- var redirectUrl = UriHelper.BuildRelative(
- context.Request.PathBase,
- "/Account/Manage/ExternalLogins",
- QueryString.Create("Action", ExternalLogins.LinkLoginCallbackAction));
-
- var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl, signInManager.UserManager.GetUserId(context.User));
- return TypedResults.Challenge(properties, [provider]);
- });
-
- var loggerFactory = endpoints.ServiceProvider.GetRequiredService();
- var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData");
-
- manageGroup.MapPost("/DownloadPersonalData", async (
- HttpContext context,
- [FromServices] UserManager userManager,
- [FromServices] AuthenticationStateProvider authenticationStateProvider) =>
- {
- var user = await userManager.GetUserAsync(context.User);
- if (user is null)
- {
- return Results.NotFound($"Unable to load user with ID '{userManager.GetUserId(context.User)}'.");
- }
-
- var userId = await userManager.GetUserIdAsync(user);
- downloadLogger.LogInformation("User with ID '{UserId}' asked for their personal data.", userId);
-
- // Only include personal data for download
- var personalData = new Dictionary();
- var personalDataProps = typeof(ApplicationUser).GetProperties().Where(
- prop => Attribute.IsDefined(prop, typeof(PersonalDataAttribute)));
- foreach (var p in personalDataProps)
- {
- personalData.Add(p.Name, p.GetValue(user)?.ToString() ?? "null");
- }
-
- var logins = await userManager.GetLoginsAsync(user);
- foreach (var l in logins)
- {
- personalData.Add($"{l.LoginProvider} external login provider key", l.ProviderKey);
- }
-
- personalData.Add("Authenticator Key", (await userManager.GetAuthenticatorKeyAsync(user))!);
- var fileBytes = JsonSerializer.SerializeToUtf8Bytes(personalData);
-
- context.Response.Headers.TryAdd("Content-Disposition", "attachment; filename=PersonalData.json");
- return TypedResults.File(fileBytes, contentType: "application/json", fileDownloadName: "PersonalData.json");
- });
-
return accountGroup;
}
}
diff --git a/RobotApp/Components/Account/Pages/ConfirmEmail.razor b/RobotApp/Components/Account/Pages/ConfirmEmail.razor
deleted file mode 100644
index 3735133..0000000
--- a/RobotApp/Components/Account/Pages/ConfirmEmail.razor
+++ /dev/null
@@ -1,48 +0,0 @@
-@page "/Account/ConfirmEmail"
-
-@using System.Text
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject IdentityRedirectManager RedirectManager
-
-Confirm email
-
-
Confirm email
-
-
-@code {
- private string? statusMessage;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromQuery]
- private string? UserId { get; set; }
-
- [SupplyParameterFromQuery]
- private string? Code { get; set; }
-
- protected override async Task OnInitializedAsync()
- {
- if (UserId is null || Code is null)
- {
- RedirectManager.RedirectTo("");
- }
-
- var user = await UserManager.FindByIdAsync(UserId);
- if (user is null)
- {
- HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
- statusMessage = $"Error loading user with ID {UserId}";
- }
- else
- {
- var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code));
- var result = await UserManager.ConfirmEmailAsync(user, code);
- statusMessage = result.Succeeded ? "Thank you for confirming your email." : "Error confirming your email.";
- }
- }
-}
diff --git a/RobotApp/Components/Account/Pages/ConfirmEmailChange.razor b/RobotApp/Components/Account/Pages/ConfirmEmailChange.razor
deleted file mode 100644
index f23f5f7..0000000
--- a/RobotApp/Components/Account/Pages/ConfirmEmailChange.razor
+++ /dev/null
@@ -1,68 +0,0 @@
-@page "/Account/ConfirmEmailChange"
-
-@using System.Text
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityRedirectManager RedirectManager
-
-Confirm email change
-
-
Confirm email change
-
-
-
-@code {
- private string? message;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromQuery]
- private string? UserId { get; set; }
-
- [SupplyParameterFromQuery]
- private string? Email { get; set; }
-
- [SupplyParameterFromQuery]
- private string? Code { get; set; }
-
- protected override async Task OnInitializedAsync()
- {
- if (UserId is null || Email is null || Code is null)
- {
- RedirectManager.RedirectToWithStatus(
- "Account/Login", "Error: Invalid email change confirmation link.", HttpContext);
- }
-
- var user = await UserManager.FindByIdAsync(UserId);
- if (user is null)
- {
- message = "Unable to find user with Id '{userId}'";
- return;
- }
-
- var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(Code));
- var result = await UserManager.ChangeEmailAsync(user, Email, code);
- if (!result.Succeeded)
- {
- message = "Error changing email.";
- return;
- }
-
- // In our UI email and user name are one and the same, so when we update the email
- // we need to update the user name.
- var setUserNameResult = await UserManager.SetUserNameAsync(user, Email);
- if (!setUserNameResult.Succeeded)
- {
- message = "Error changing user name.";
- return;
- }
-
- await SignInManager.RefreshSignInAsync(user);
- message = "Thank you for confirming your email change.";
- }
-}
diff --git a/RobotApp/Components/Account/Pages/ExternalLogin.razor b/RobotApp/Components/Account/Pages/ExternalLogin.razor
deleted file mode 100644
index 11a4d15..0000000
--- a/RobotApp/Components/Account/Pages/ExternalLogin.razor
+++ /dev/null
@@ -1,205 +0,0 @@
-@page "/Account/ExternalLogin"
-
-@using System.ComponentModel.DataAnnotations
-@using System.Security.Claims
-@using System.Text
-@using System.Text.Encodings.Web
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject SignInManager SignInManager
-@inject UserManager UserManager
-@inject IUserStore UserStore
-@inject IEmailSender EmailSender
-@inject NavigationManager NavigationManager
-@inject IdentityRedirectManager RedirectManager
-@inject ILogger Logger
-
-Register
-
-
-
Register
-
Associate your @ProviderDisplayName account.
-
-
-
- You've successfully authenticated with @ProviderDisplayName.
- Please enter an email address for this site below and click the Register button to finish
- logging in.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code {
- public const string LoginCallbackAction = "LoginCallback";
-
- private string? message;
- private ExternalLoginInfo? externalLoginInfo;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- [SupplyParameterFromQuery]
- private string? RemoteError { get; set; }
-
- [SupplyParameterFromQuery]
- private string? ReturnUrl { get; set; }
-
- [SupplyParameterFromQuery]
- private string? Action { get; set; }
-
- private string? ProviderDisplayName => externalLoginInfo?.ProviderDisplayName;
-
- protected override async Task OnInitializedAsync()
- {
- if (RemoteError is not null)
- {
- RedirectManager.RedirectToWithStatus("Account/Login", $"Error from external provider: {RemoteError}", HttpContext);
- }
-
- var info = await SignInManager.GetExternalLoginInfoAsync();
- if (info is null)
- {
- RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext);
- }
-
- externalLoginInfo = info;
-
- if (HttpMethods.IsGet(HttpContext.Request.Method))
- {
- if (Action == LoginCallbackAction)
- {
- await OnLoginCallbackAsync();
- return;
- }
-
- // We should only reach this page via the login callback, so redirect back to
- // the login page if we get here some other way.
- RedirectManager.RedirectTo("Account/Login");
- }
- }
-
- private async Task OnLoginCallbackAsync()
- {
- if (externalLoginInfo is null)
- {
- RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information.", HttpContext);
- }
-
- // Sign in the user with this external login provider if the user already has a login.
- var result = await SignInManager.ExternalLoginSignInAsync(
- externalLoginInfo.LoginProvider,
- externalLoginInfo.ProviderKey,
- isPersistent: false,
- bypassTwoFactor: true);
-
- if (result.Succeeded)
- {
- Logger.LogInformation(
- "{Name} logged in with {LoginProvider} provider.",
- externalLoginInfo.Principal.Identity?.Name,
- externalLoginInfo.LoginProvider);
- RedirectManager.RedirectTo(ReturnUrl);
- }
- else if (result.IsLockedOut)
- {
- RedirectManager.RedirectTo("Account/Lockout");
- }
-
- // If the user does not have an account, then ask the user to create an account.
- if (externalLoginInfo.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
- {
- Input.Email = externalLoginInfo.Principal.FindFirstValue(ClaimTypes.Email) ?? "";
- }
- }
-
- private async Task OnValidSubmitAsync()
- {
- if (externalLoginInfo is null)
- {
- RedirectManager.RedirectToWithStatus("Account/Login", "Error loading external login information during confirmation.", HttpContext);
- }
-
- var emailStore = GetEmailStore();
- var user = CreateUser();
-
- await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
- await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
-
- var result = await UserManager.CreateAsync(user);
- if (result.Succeeded)
- {
- result = await UserManager.AddLoginAsync(user, externalLoginInfo);
- if (result.Succeeded)
- {
- Logger.LogInformation("User created an account using {Name} provider.", externalLoginInfo.LoginProvider);
-
- var userId = await UserManager.GetUserIdAsync(user);
- var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
- code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
-
- var callbackUrl = NavigationManager.GetUriWithQueryParameters(
- NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
- new Dictionary { ["userId"] = userId, ["code"] = code });
- await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
-
- // 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)
- {
- RedirectManager.RedirectTo("Account/RegisterConfirmation", new() { ["email"] = Input.Email });
- }
-
- await SignInManager.SignInAsync(user, isPersistent: false, externalLoginInfo.LoginProvider);
- RedirectManager.RedirectTo(ReturnUrl);
- }
- }
-
- message = $"Error: {string.Join(",", result.Errors.Select(error => error.Description))}";
- }
-
- private ApplicationUser CreateUser()
- {
- try
- {
- return Activator.CreateInstance();
- }
- catch
- {
- throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
- $"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor");
- }
- }
-
- private IUserEmailStore GetEmailStore()
- {
- if (!UserManager.SupportsUserEmail)
- {
- throw new NotSupportedException("The default UI requires a user store with email support.");
- }
- return (IUserEmailStore)UserStore;
- }
-
- private sealed class InputModel
- {
- [Required]
- [EmailAddress]
- public string Email { get; set; } = "";
- }
-}
diff --git a/RobotApp/Components/Account/Pages/ForgotPassword.razor b/RobotApp/Components/Account/Pages/ForgotPassword.razor
deleted file mode 100644
index 0629665..0000000
--- a/RobotApp/Components/Account/Pages/ForgotPassword.razor
+++ /dev/null
@@ -1,68 +0,0 @@
-@page "/Account/ForgotPassword"
-
-@using System.ComponentModel.DataAnnotations
-@using System.Text
-@using System.Text.Encodings.Web
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject IEmailSender EmailSender
-@inject NavigationManager NavigationManager
-@inject IdentityRedirectManager RedirectManager
-
-Forgot your password?
-
-
Forgot your password?
-
Enter your email.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code {
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- private async Task OnValidSubmitAsync()
- {
- var user = await UserManager.FindByEmailAsync(Input.Email);
- if (user is null || !(await UserManager.IsEmailConfirmedAsync(user)))
- {
- // Don't reveal that the user does not exist or is not confirmed
- RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation");
- }
-
- // For more information on how to enable account confirmation and password reset please
- // visit https://go.microsoft.com/fwlink/?LinkID=532713
- var code = await UserManager.GeneratePasswordResetTokenAsync(user);
- code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
- var callbackUrl = NavigationManager.GetUriWithQueryParameters(
- NavigationManager.ToAbsoluteUri("Account/ResetPassword").AbsoluteUri,
- new Dictionary { ["code"] = code });
-
- await EmailSender.SendPasswordResetLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
-
- RedirectManager.RedirectTo("Account/ForgotPasswordConfirmation");
- }
-
- private sealed class InputModel
- {
- [Required]
- [EmailAddress]
- public string Email { get; set; } = "";
- }
-}
diff --git a/RobotApp/Components/Account/Pages/ForgotPasswordConfirmation.razor b/RobotApp/Components/Account/Pages/ForgotPasswordConfirmation.razor
deleted file mode 100644
index a771a3a..0000000
--- a/RobotApp/Components/Account/Pages/ForgotPasswordConfirmation.razor
+++ /dev/null
@@ -1,8 +0,0 @@
-@page "/Account/ForgotPasswordConfirmation"
-
-Forgot password confirmation
-
-
Forgot password confirmation
-
- Please check your email to reset your password.
-
-
-@code {
- private string? message;
- private ApplicationUser user = default!;
-
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- [SupplyParameterFromQuery]
- private string? ReturnUrl { get; set; }
-
- [SupplyParameterFromQuery]
- private bool RememberMe { get; set; }
-
- protected override async Task OnInitializedAsync()
- {
- // Ensure the user has gone through the username & password screen first
- user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
- throw new InvalidOperationException("Unable to load two-factor authentication user.");
- }
-
- private async Task OnValidSubmitAsync()
- {
- var authenticatorCode = Input.TwoFactorCode!.Replace(" ", string.Empty).Replace("-", string.Empty);
- 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.", userId);
- RedirectManager.RedirectTo(ReturnUrl);
- }
- else if (result.IsLockedOut)
- {
- Logger.LogWarning("User with ID '{UserId}' account locked out.", userId);
- RedirectManager.RedirectTo("Account/Lockout");
- }
- else
- {
- Logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", userId);
- message = "Error: Invalid authenticator code.";
- }
- }
-
- private sealed class InputModel
- {
- [Required]
- [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
- [DataType(DataType.Text)]
- [Display(Name = "Authenticator code")]
- public string? TwoFactorCode { get; set; }
-
- [Display(Name = "Remember this machine")]
- public bool RememberMachine { get; set; }
- }
-}
diff --git a/RobotApp/Components/Account/Pages/LoginWithRecoveryCode.razor b/RobotApp/Components/Account/Pages/LoginWithRecoveryCode.razor
deleted file mode 100644
index 5719e8a..0000000
--- a/RobotApp/Components/Account/Pages/LoginWithRecoveryCode.razor
+++ /dev/null
@@ -1,85 +0,0 @@
-@page "/Account/LoginWithRecoveryCode"
-
-@using System.ComponentModel.DataAnnotations
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject SignInManager SignInManager
-@inject UserManager UserManager
-@inject IdentityRedirectManager RedirectManager
-@inject ILogger Logger
-
-Recovery code verification
-
-
Recovery code verification
-
-
-
- You have requested to log in with a recovery code. This login will not be remembered until you provide
- an authenticator app code at log in or disable 2FA and log in again.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code {
- private string? message;
- private ApplicationUser user = default!;
-
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- [SupplyParameterFromQuery]
- private string? ReturnUrl { get; set; }
-
- protected override async Task OnInitializedAsync()
- {
- // Ensure the user has gone through the username & password screen first
- user = await SignInManager.GetTwoFactorAuthenticationUserAsync() ??
- throw new InvalidOperationException("Unable to load two-factor authentication user.");
- }
-
- private async Task OnValidSubmitAsync()
- {
- var recoveryCode = Input.RecoveryCode.Replace(" ", string.Empty);
-
- 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.", userId);
- RedirectManager.RedirectTo(ReturnUrl);
- }
- else if (result.IsLockedOut)
- {
- Logger.LogWarning("User account locked out.");
- RedirectManager.RedirectTo("Account/Lockout");
- }
- else
- {
- Logger.LogWarning("Invalid recovery code entered for user with ID '{UserId}' ", userId);
- message = "Error: Invalid recovery code entered.";
- }
- }
-
- private sealed class InputModel
- {
- [Required]
- [DataType(DataType.Text)]
- [Display(Name = "Recovery Code")]
- public string RecoveryCode { get; set; } = "";
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/ChangePassword.razor b/RobotApp/Components/Account/Pages/Manage/ChangePassword.razor
deleted file mode 100644
index 096cf8f..0000000
--- a/RobotApp/Components/Account/Pages/Manage/ChangePassword.razor
+++ /dev/null
@@ -1,96 +0,0 @@
-@page "/Account/Manage/ChangePassword"
-
-@using System.ComponentModel.DataAnnotations
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityUserAccessor UserAccessor
-@inject IdentityRedirectManager RedirectManager
-@inject ILogger Logger
-
-Change password
-
-
Change password
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code {
- private string? message;
- private ApplicationUser user = default!;
- private bool hasPassword;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- protected override async Task OnInitializedAsync()
- {
- user = await UserAccessor.GetRequiredUserAsync(HttpContext);
- hasPassword = await UserManager.HasPasswordAsync(user);
- if (!hasPassword)
- {
- RedirectManager.RedirectTo("Account/Manage/SetPassword");
- }
- }
-
- private async Task OnValidSubmitAsync()
- {
- var changePasswordResult = await UserManager.ChangePasswordAsync(user, Input.OldPassword, Input.NewPassword);
- if (!changePasswordResult.Succeeded)
- {
- message = $"Error: {string.Join(",", changePasswordResult.Errors.Select(error => error.Description))}";
- return;
- }
-
- await SignInManager.RefreshSignInAsync(user);
- Logger.LogInformation("User changed their password successfully.");
-
- RedirectManager.RedirectToCurrentPageWithStatus("Your password has been changed", HttpContext);
- }
-
- private sealed class InputModel
- {
- [Required]
- [DataType(DataType.Password)]
- [Display(Name = "Current password")]
- public string OldPassword { get; set; } = "";
-
- [Required]
- [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
- [DataType(DataType.Password)]
- [Display(Name = "New password")]
- public string NewPassword { get; set; } = "";
-
- [DataType(DataType.Password)]
- [Display(Name = "Confirm new password")]
- [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
- public string ConfirmPassword { get; set; } = "";
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/DeletePersonalData.razor b/RobotApp/Components/Account/Pages/Manage/DeletePersonalData.razor
deleted file mode 100644
index 751c6eb..0000000
--- a/RobotApp/Components/Account/Pages/Manage/DeletePersonalData.razor
+++ /dev/null
@@ -1,86 +0,0 @@
-@page "/Account/Manage/DeletePersonalData"
-
-@using System.ComponentModel.DataAnnotations
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityUserAccessor UserAccessor
-@inject IdentityRedirectManager RedirectManager
-@inject ILogger Logger
-
-Delete Personal Data
-
-
-
-
Delete Personal Data
-
-
-
- Deleting this data will permanently remove your account, and this cannot be recovered.
-
- Disabling 2FA does not change the keys used in authenticator apps. If you wish to change the key
- used in an authenticator app you should reset your authenticator keys.
-
-
-
-
-
-
-
-@code {
- private ApplicationUser user = default!;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- protected override async Task OnInitializedAsync()
- {
- user = await UserAccessor.GetRequiredUserAsync(HttpContext);
-
- if (HttpMethods.IsGet(HttpContext.Request.Method) && !await UserManager.GetTwoFactorEnabledAsync(user))
- {
- throw new InvalidOperationException("Cannot disable 2FA for user as it's not currently enabled.");
- }
- }
-
- private async Task OnSubmitAsync()
- {
- var disable2faResult = await UserManager.SetTwoFactorEnabledAsync(user, false);
- if (!disable2faResult.Succeeded)
- {
- throw new InvalidOperationException("Unexpected error occurred disabling 2FA.");
- }
-
- var userId = await UserManager.GetUserIdAsync(user);
- Logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", userId);
- RedirectManager.RedirectToWithStatus(
- "Account/Manage/TwoFactorAuthentication",
- "2fa has been disabled. You can reenable 2fa when you setup an authenticator app",
- HttpContext);
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/Email.razor b/RobotApp/Components/Account/Pages/Manage/Email.razor
deleted file mode 100644
index 3696e62..0000000
--- a/RobotApp/Components/Account/Pages/Manage/Email.razor
+++ /dev/null
@@ -1,123 +0,0 @@
-@page "/Account/Manage/Email"
-
-@using System.ComponentModel.DataAnnotations
-@using System.Text
-@using System.Text.Encodings.Web
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject IEmailSender EmailSender
-@inject IdentityUserAccessor UserAccessor
-@inject NavigationManager NavigationManager
-
-Manage email
-
-
- Once you have scanned the QR code or input the key above, your two factor authentication app will provide you
- with a unique code. Enter the code in the confirmation box below.
-
- If you lose your device and don't have the recovery codes you will lose access to your account.
-
-
- Generating new recovery codes does not change the keys used in authenticator apps. If you wish to change the key
- used in an authenticator app you should reset your authenticator keys.
-
-
-
-
-
-}
-
-@code {
- private string? message;
- private ApplicationUser user = default!;
- private IEnumerable? recoveryCodes;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- protected override async Task OnInitializedAsync()
- {
- user = await UserAccessor.GetRequiredUserAsync(HttpContext);
-
- var isTwoFactorEnabled = await UserManager.GetTwoFactorEnabledAsync(user);
- if (!isTwoFactorEnabled)
- {
- throw new InvalidOperationException("Cannot generate recovery codes for user because they do not have 2FA enabled.");
- }
- }
-
- private async Task OnSubmitAsync()
- {
- var userId = await UserManager.GetUserIdAsync(user);
- recoveryCodes = await UserManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
- message = "You have generated new recovery codes.";
-
- Logger.LogInformation("User with ID '{UserId}' has generated new 2FA recovery codes.", userId);
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/Index.razor b/RobotApp/Components/Account/Pages/Manage/Index.razor
deleted file mode 100644
index 77f4f19..0000000
--- a/RobotApp/Components/Account/Pages/Manage/Index.razor
+++ /dev/null
@@ -1,77 +0,0 @@
-@page "/Account/Manage"
-
-@using System.ComponentModel.DataAnnotations
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityUserAccessor UserAccessor
-@inject IdentityRedirectManager RedirectManager
-
-Profile
-
-
-
- If you reset your authenticator key your authenticator app will not work until you reconfigure it.
-
-
- This process disables 2FA until you verify your authenticator app.
- If you do not complete your authenticator app configuration you may lose access to your account.
-
-
-
-
-
-
-@code {
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- private async Task OnSubmitAsync()
- {
- var user = await UserAccessor.GetRequiredUserAsync(HttpContext);
- await UserManager.SetTwoFactorEnabledAsync(user, false);
- await UserManager.ResetAuthenticatorKeyAsync(user);
- var userId = await UserManager.GetUserIdAsync(user);
- Logger.LogInformation("User with ID '{UserId}' has reset their authentication app key.", userId);
-
- await SignInManager.RefreshSignInAsync(user);
-
- RedirectManager.RedirectToWithStatus(
- "Account/Manage/EnableAuthenticator",
- "Your authenticator app key has been reset, you will need to configure your authenticator app using the new key.",
- HttpContext);
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/SetPassword.razor b/RobotApp/Components/Account/Pages/Manage/SetPassword.razor
deleted file mode 100644
index 3048d81..0000000
--- a/RobotApp/Components/Account/Pages/Manage/SetPassword.razor
+++ /dev/null
@@ -1,87 +0,0 @@
-@page "/Account/Manage/SetPassword"
-
-@using System.ComponentModel.DataAnnotations
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityUserAccessor UserAccessor
-@inject IdentityRedirectManager RedirectManager
-
-Set password
-
-
Set your password
-
-
- You do not have a local username/password for this site. Add a local
- account so you can log in without an external login.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-@code {
- private string? message;
- private ApplicationUser user = default!;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromForm]
- private InputModel Input { get; set; } = new();
-
- protected override async Task OnInitializedAsync()
- {
- user = await UserAccessor.GetRequiredUserAsync(HttpContext);
-
- var hasPassword = await UserManager.HasPasswordAsync(user);
- if (hasPassword)
- {
- RedirectManager.RedirectTo("Account/Manage/ChangePassword");
- }
- }
-
- private async Task OnValidSubmitAsync()
- {
- var addPasswordResult = await UserManager.AddPasswordAsync(user, Input.NewPassword!);
- if (!addPasswordResult.Succeeded)
- {
- message = $"Error: {string.Join(",", addPasswordResult.Errors.Select(error => error.Description))}";
- return;
- }
-
- await SignInManager.RefreshSignInAsync(user);
- RedirectManager.RedirectToCurrentPageWithStatus("Your password has been set.", HttpContext);
- }
-
- private sealed class InputModel
- {
- [Required]
- [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
- [DataType(DataType.Password)]
- [Display(Name = "New password")]
- public string? NewPassword { get; set; }
-
- [DataType(DataType.Password)]
- [Display(Name = "Confirm new password")]
- [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
- public string? ConfirmPassword { get; set; }
- }
-}
diff --git a/RobotApp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor b/RobotApp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor
deleted file mode 100644
index c4a1052..0000000
--- a/RobotApp/Components/Account/Pages/Manage/TwoFactorAuthentication.razor
+++ /dev/null
@@ -1,101 +0,0 @@
-@page "/Account/Manage/TwoFactorAuthentication"
-
-@using Microsoft.AspNetCore.Http.Features
-@using Microsoft.AspNetCore.Identity
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject SignInManager SignInManager
-@inject IdentityUserAccessor UserAccessor
-@inject IdentityRedirectManager RedirectManager
-
-Two-factor authentication (2FA)
-
-
-
Two-factor authentication (2FA)
-@if (canTrack)
-{
- if (is2faEnabled)
- {
- if (recoveryCodesLeft == 0)
- {
-
+
@code {
+ private string errorMessage = string.Empty;
+
private IEnumerable? identityErrors;
[SupplyParameterFromForm]
@@ -69,9 +67,9 @@
{
var user = CreateUser();
- await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
- var emailStore = GetEmailStore();
- await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
+ await UserStore.SetUserNameAsync(user, Input.UserName, CancellationToken.None);
+ user.NormalizedUserName = Input.UserName.ToUpperInvariant();
+ user.EmailConfirmed = true;
var result = await UserManager.CreateAsync(user, Input.Password);
if (!result.Succeeded)
@@ -82,22 +80,6 @@
Logger.LogInformation("User created a new account with password.");
- var userId = await UserManager.GetUserIdAsync(user);
- var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
- code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
- var callbackUrl = NavigationManager.GetUriWithQueryParameters(
- NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
- new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
-
- await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
-
- if (UserManager.Options.SignIn.RequireConfirmedAccount)
- {
- RedirectManager.RedirectTo(
- "Account/RegisterConfirmation",
- new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl });
- }
-
await SignInManager.SignInAsync(user, isPersistent: false);
RedirectManager.RedirectTo(ReturnUrl);
}
@@ -115,21 +97,12 @@
}
}
- private IUserEmailStore GetEmailStore()
- {
- if (!UserManager.SupportsUserEmail)
- {
- throw new NotSupportedException("The default UI requires a user store with email support.");
- }
- return (IUserEmailStore)UserStore;
- }
private sealed class InputModel
{
[Required]
- [EmailAddress]
- [Display(Name = "Email")]
- public string Email { get; set; } = "";
+ [Display(Name = "UserName")]
+ public string UserName { get; set; } = "";
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
@@ -142,4 +115,4 @@
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; } = "";
}
-}
+}
\ No newline at end of file
diff --git a/RobotApp/Components/Account/Pages/RegisterConfirmation.razor b/RobotApp/Components/Account/Pages/RegisterConfirmation.razor
deleted file mode 100644
index f903e0e..0000000
--- a/RobotApp/Components/Account/Pages/RegisterConfirmation.razor
+++ /dev/null
@@ -1,68 +0,0 @@
-@page "/Account/RegisterConfirmation"
-
-@using System.Text
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject IEmailSender EmailSender
-@inject NavigationManager NavigationManager
-@inject IdentityRedirectManager RedirectManager
-
-Register confirmation
-
-
Register confirmation
-
-
-
-@if (emailConfirmationLink is not null)
-{
-
- This app does not currently have a real email sender registered, see these docs for how to configure a real email sender.
- Normally this would be emailed: Click here to confirm your account
-
-}
-else
-{
-
Please check your email to confirm your account.
-}
-
-@code {
- private string? emailConfirmationLink;
- private string? statusMessage;
-
- [CascadingParameter]
- private HttpContext HttpContext { get; set; } = default!;
-
- [SupplyParameterFromQuery]
- private string? Email { get; set; }
-
- [SupplyParameterFromQuery]
- private string? ReturnUrl { get; set; }
-
- protected override async Task OnInitializedAsync()
- {
- if (Email is null)
- {
- RedirectManager.RedirectTo("");
- }
-
- var user = await UserManager.FindByEmailAsync(Email);
- if (user is null)
- {
- HttpContext.Response.StatusCode = StatusCodes.Status404NotFound;
- statusMessage = "Error finding user for unspecified email";
- }
- else if (EmailSender is IdentityNoOpEmailSender)
- {
- // Once you add a real email sender, you should remove this code that lets you confirm the account
- var userId = await UserManager.GetUserIdAsync(user);
- var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
- code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
- emailConfirmationLink = NavigationManager.GetUriWithQueryParameters(
- NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
- new Dictionary { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
- }
- }
-}
diff --git a/RobotApp/Components/Account/Pages/ResendEmailConfirmation.razor b/RobotApp/Components/Account/Pages/ResendEmailConfirmation.razor
deleted file mode 100644
index c4fe4a5..0000000
--- a/RobotApp/Components/Account/Pages/ResendEmailConfirmation.razor
+++ /dev/null
@@ -1,68 +0,0 @@
-@page "/Account/ResendEmailConfirmation"
-
-@using System.ComponentModel.DataAnnotations
-@using System.Text
-@using System.Text.Encodings.Web
-@using Microsoft.AspNetCore.Identity
-@using Microsoft.AspNetCore.WebUtilities
-@using RobotApp.Data
-
-@inject UserManager UserManager
-@inject IEmailSender EmailSender
-@inject NavigationManager NavigationManager
-@inject IdentityRedirectManager RedirectManager
-
-Resend email confirmation
-
-