feaaf76f46
Move some of the shared components
146 lines
5.2 KiB
Plaintext
146 lines
5.2 KiB
Plaintext
@page "/login"
|
|
@using WebApp.Components.Layout
|
|
@layout EmptyLayout
|
|
@using System.ComponentModel.DataAnnotations
|
|
@using WebApp.Authentication
|
|
@inject NavigationManager Navigation
|
|
@inject LoginRateLimitService RateLimitService
|
|
@inject IHttpContextAccessor HttpContextAccessor
|
|
|
|
<div class="d-flex justify-center align-center" style="min-height: 100vh;">
|
|
<MudPaper Elevation="3" Class="pa-8" MaxWidth="400px" Style="width: 100%;">
|
|
<MudText Typo="Typo.h4" Align="Align.Center" GutterBottom="true">
|
|
TSA Chapter Organizer
|
|
</MudText>
|
|
<MudText Typo="Typo.h6" Align="Align.Center" GutterBottom="true" Class="mb-6">
|
|
Sign In
|
|
</MudText>
|
|
|
|
@if (!string.IsNullOrEmpty(_errorMessage))
|
|
{
|
|
<MudAlert Severity="Severity.Error" Class="mb-4">@_errorMessage</MudAlert>
|
|
}
|
|
|
|
<EditForm Model="@_loginModel" OnValidSubmit="HandleLogin">
|
|
<DataAnnotationsValidator />
|
|
|
|
<MudTextField @bind-Value="_loginModel.Email"
|
|
Label="Email"
|
|
Variant="Variant.Outlined"
|
|
InputType="InputType.Email"
|
|
Required="true"
|
|
Class="mb-4"
|
|
Disabled="@_isSubmitting"
|
|
For="@(() => _loginModel.Email)" />
|
|
|
|
<MudTextField @bind-Value="_loginModel.Password"
|
|
Label="Password"
|
|
Variant="Variant.Outlined"
|
|
InputType="@_passwordInput"
|
|
Required="true"
|
|
Class="mb-4"
|
|
Disabled="@_isSubmitting"
|
|
Adornment="Adornment.End"
|
|
AdornmentIcon="@_passwordInputIcon"
|
|
OnAdornmentClick="TogglePasswordVisibility"
|
|
For="@(() => _loginModel.Password)" />
|
|
|
|
<div class="d-flex justify-space-between align-center mb-4">
|
|
<MudCheckBox @bind-Value="_loginModel.RememberMe"
|
|
Label="Remember me?"
|
|
Color="Color.Primary"
|
|
Disabled="@_isSubmitting" />
|
|
</div>
|
|
|
|
<MudButton ButtonType="ButtonType.Submit"
|
|
Variant="Variant.Filled"
|
|
Color="Color.Primary"
|
|
Size="Size.Large"
|
|
FullWidth="true"
|
|
Disabled="@_isSubmitting">
|
|
@if (_isSubmitting)
|
|
{
|
|
<MudProgressCircular Size="Size.Small" Indeterminate="true" Class="mr-2" />
|
|
<span>Signing in...</span>
|
|
}
|
|
else
|
|
{
|
|
<span>Sign In</span>
|
|
}
|
|
</MudButton>
|
|
</EditForm>
|
|
</MudPaper>
|
|
</div>
|
|
|
|
@code {
|
|
private LoginModel _loginModel = new();
|
|
private string? _errorMessage;
|
|
private bool _isSubmitting = false;
|
|
private bool _passwordVisibility;
|
|
private InputType _passwordInput = InputType.Password;
|
|
private string _passwordInputIcon = Icons.Material.Filled.VisibilityOff;
|
|
|
|
private class LoginModel
|
|
{
|
|
[Required(ErrorMessage = "Email is required")]
|
|
[EmailAddress(ErrorMessage = "Invalid email format")]
|
|
public string Email { get; set; } = string.Empty;
|
|
|
|
[Required(ErrorMessage = "Password is required")]
|
|
[MinLength(8, ErrorMessage = "Password must be at least 8 characters")]
|
|
public string Password { get; set; } = string.Empty;
|
|
|
|
public bool RememberMe { get; set; }
|
|
}
|
|
|
|
private void TogglePasswordVisibility()
|
|
{
|
|
if (_isSubmitting) return;
|
|
|
|
_passwordVisibility = !_passwordVisibility;
|
|
_passwordInputIcon = _passwordVisibility
|
|
? Icons.Material.Filled.Visibility
|
|
: Icons.Material.Filled.VisibilityOff;
|
|
_passwordInput = _passwordVisibility
|
|
? InputType.Text
|
|
: InputType.Password;
|
|
}
|
|
|
|
private void HandleLogin()
|
|
{
|
|
_isSubmitting = true;
|
|
_errorMessage = null;
|
|
|
|
try
|
|
{
|
|
// Get client IP address
|
|
var ipAddress = HttpContextAccessor.HttpContext?.Connection.RemoteIpAddress?.ToString()
|
|
?? "unknown";
|
|
|
|
// Check rate limiting
|
|
if (RateLimitService.IsLockedOut(ipAddress))
|
|
{
|
|
var remaining = RateLimitService.GetRemainingLockoutTime(ipAddress);
|
|
_errorMessage = $"Too many failed attempts. Please try again in {remaining?.Minutes ?? 15} minutes.";
|
|
return;
|
|
}
|
|
|
|
// Navigate to controller endpoint with credentials
|
|
var uri = $"/Auth/CookieLogin?email={Uri.EscapeDataString(_loginModel.Email)}" +
|
|
$"&password={Uri.EscapeDataString(_loginModel.Password)}" +
|
|
$"&rememberMe={_loginModel.RememberMe}";
|
|
|
|
Navigation.NavigateTo(uri, forceLoad: true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_errorMessage = "An error occurred during login. Please try again.";
|
|
Console.WriteLine($"Login error: {ex}");
|
|
}
|
|
finally
|
|
{
|
|
_isSubmitting = false;
|
|
}
|
|
}
|
|
}
|