@page "/login" @layout EmptyLayout @using System.ComponentModel.DataAnnotations @using WebApp.Authentication @using Microsoft.AspNetCore.Antiforgery @using Microsoft.AspNetCore.Components.Web @using MudInputType = MudBlazor.InputType @using Microsoft.JSInterop @using Microsoft.AspNetCore.WebUtilities @inject NavigationManager Navigation @inject LoginRateLimitService RateLimitService @inject IHttpContextAccessor HttpContextAccessor @inject IAntiforgery Antiforgery @inject IJSRuntime JS
TSA Chapter Organizer Sign In @if (!string.IsNullOrEmpty(_errorMessage)) { @_errorMessage }
Sign In
@code { private LoginModel _loginModel = new(); private string? _errorMessage; private bool _passwordVisibility; private MudInputType _passwordInput = MudInputType.Password; private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff; private string _antiforgeryToken = string.Empty; private string? _returnUrl; protected override void OnInitialized() { // Generate antiforgery token var httpContext = HttpContextAccessor.HttpContext; if (httpContext != null) { var tokenSet = Antiforgery.GetAndStoreTokens(httpContext); _antiforgeryToken = tokenSet.RequestToken ?? string.Empty; } // Check for error message and returnUrl from query parameters var uri = new Uri(Navigation.Uri); var queryParams = QueryHelpers.ParseQuery(uri.Query); if (queryParams.TryGetValue("error", out var errorValue)) { _errorMessage = errorValue.ToString(); } if (queryParams.TryGetValue("returnUrl", out var returnUrlValue)) { _returnUrl = returnUrlValue.ToString(); } } private class LoginModel { [Required(ErrorMessage = "Email is required")] [EmailAddress(ErrorMessage = "Invalid email format")] [MaxLength(100, ErrorMessage = "Email must be less than 100 characters")] [RegularExpression(@"^[^@\s]+@[^@\s]+\.[^@\s]+$", ErrorMessage = "Please enter a valid email address")] public string Email { get; set; } = string.Empty; [Required(ErrorMessage = "Password is required")] [MinLength(8, ErrorMessage = "Password must be at least 8 characters")] [MaxLength(100, ErrorMessage = "Password must be less than 100 characters")] [DataType(DataType.Password)] public string Password { get; set; } = string.Empty; public bool RememberMe { get; set; } } private void TogglePasswordVisibility() { _passwordVisibility = !_passwordVisibility; _passwordInputIcon = _passwordVisibility ? Icons.Material.Filled.Visibility : Icons.Material.Filled.VisibilityOff; _passwordInput = _passwordVisibility ? MudInputType.Text : MudInputType.Password; } private async Task HandleKeyDown(KeyboardEventArgs e) { if (e.Key == "Enter") { // Blur the active element to ensure MudTextField bindings update await JS.InvokeVoidAsync("eval", "document.activeElement.blur()"); // Small delay to allow bindings to process await Task.Delay(50); // Now submit the form await HandleFormSubmit(); } } private async Task HandleFormSubmit() { // Update hidden inputs with current model values, then submit the form var returnUrlValue = string.IsNullOrEmpty(_returnUrl) ? "" : System.Text.Json.JsonSerializer.Serialize(_returnUrl); await JS.InvokeVoidAsync("eval", $@" document.getElementById('emailInput').value = {System.Text.Json.JsonSerializer.Serialize(_loginModel.Email)}; document.getElementById('passwordInput').value = {System.Text.Json.JsonSerializer.Serialize(_loginModel.Password)}; document.getElementById('rememberMeInput').value = '{_loginModel.RememberMe.ToString().ToLower()}'; document.getElementById('returnUrlInput').value = {returnUrlValue}; document.getElementById('loginForm').submit(); "); } }