@page "/login"
@layout EmptyLayout
@inject IAuthService AuthService
@inject ITwoFactorService TwoFactorService
@inject ISiteSettingsService SiteSettingsService
@inject UserSessionService UserSession
@inject NavigationManager Navigation
@inject IJSRuntime JS
Login - Reifen Späth Lagerverwaltung
@if (!showTwoFactorStep)
{
Anmelden
}
else
{
@* 2FA Code Entry Step *@
Zwei-Faktor-Authentifizierung
Geben Sie den 6-stelligen Code aus Ihrer Authenticator-App ein.
}
@code {
private string username = "";
private string password = "";
private bool isLoading;
private string? errorMessage;
// 2FA state
private bool showTwoFactorStep;
private string twoFactorCode = "";
private AppUser? pendingUser;
private bool emailFallbackAvailable;
private bool isSendingEmail;
private string? emailFallbackMessage;
private string emailFallbackClass = "";
private string? pendingEmailCode;
private DateTime? emailCodeExpiry;
private bool CanLogin =>
!string.IsNullOrWhiteSpace(username) &&
!string.IsNullOrWhiteSpace(password);
protected override void OnInitialized()
{
if (UserSession.IsAuthenticated)
{
Navigation.NavigateTo("/tire-storage");
}
}
private void ClearError()
{
if (!string.IsNullOrEmpty(errorMessage))
{
errorMessage = null;
StateHasChanged();
}
}
private async Task HandleLogin()
{
if (isLoading || !CanLogin) return;
errorMessage = null;
isLoading = true;
StateHasChanged();
try
{
await Task.Delay(250);
var user = await AuthService.LoginAsync(username.Trim(), password);
if (user != null)
{
// Check if 2FA is required
var tfSettings = await SiteSettingsService.GetTwoFactorSettingsAsync();
if (tfSettings.IsEnabled && user.TwoFactorEnabled && !string.IsNullOrEmpty(user.TwoFactorSecret))
{
pendingUser = user;
showTwoFactorStep = true;
twoFactorCode = "";
emailFallbackAvailable = tfSettings.EmailFallbackEnabled;
emailFallbackMessage = null;
pendingEmailCode = null;
}
else
{
await CompleteLogin(user);
}
}
else
{
errorMessage = "Ungültiger Benutzername oder Passwort.";
password = "";
try
{
await JS.InvokeVoidAsync("qrScanner.playSound", "error");
}
catch { }
}
}
catch (Exception)
{
errorMessage = "Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.";
}
finally
{
isLoading = false;
StateHasChanged();
}
}
private async Task HandleTwoFactorSubmit()
{
if (isLoading || pendingUser == null || twoFactorCode.Length != 6) return;
errorMessage = null;
isLoading = true;
StateHasChanged();
try
{
await Task.Delay(200);
bool valid = false;
// Check email fallback code first
if (pendingEmailCode != null && emailCodeExpiry.HasValue && DateTime.UtcNow < emailCodeExpiry.Value)
{
if (System.Security.Cryptography.CryptographicOperations.FixedTimeEquals(
System.Text.Encoding.UTF8.GetBytes(pendingEmailCode),
System.Text.Encoding.UTF8.GetBytes(twoFactorCode)))
{
valid = true;
}
}
// Check TOTP code
if (!valid && !string.IsNullOrEmpty(pendingUser.TwoFactorSecret))
{
valid = TwoFactorService.ValidateTotpCode(pendingUser.TwoFactorSecret, twoFactorCode);
}
if (valid)
{
await CompleteLogin(pendingUser);
}
else
{
errorMessage = "Ungültiger Code. Bitte versuchen Sie es erneut.";
twoFactorCode = "";
try { await JS.InvokeVoidAsync("qrScanner.playSound", "error"); } catch { }
}
}
catch (Exception)
{
errorMessage = "Ein Fehler ist aufgetreten.";
}
finally
{
isLoading = false;
StateHasChanged();
}
}
private async Task SendEmailFallbackCode()
{
if (pendingUser == null || isSendingEmail) return;
isSendingEmail = true;
emailFallbackMessage = null;
StateHasChanged();
try
{
var smtpSettings = await SiteSettingsService.GetSmtpSettingsAsync();
if (!smtpSettings.IsEnabled || !smtpSettings.IsConfigured)
{
emailFallbackMessage = "E-Mail-Versand nicht konfiguriert.";
emailFallbackClass = "text-danger";
return;
}
var code = TwoFactorService.GenerateEmailCode();
var sent = await TwoFactorService.SendEmailCodeAsync(smtpSettings.FromEmail, code, pendingUser.Username);
if (sent)
{
pendingEmailCode = code;
emailCodeExpiry = DateTime.UtcNow.AddMinutes(5);
emailFallbackMessage = "Code wurde gesendet.";
emailFallbackClass = "text-success";
}
else
{
emailFallbackMessage = "Fehler beim E-Mail-Versand.";
emailFallbackClass = "text-danger";
}
}
catch
{
emailFallbackMessage = "Fehler beim E-Mail-Versand.";
emailFallbackClass = "text-danger";
}
finally
{
isSendingEmail = false;
StateHasChanged();
}
}
private async Task CompleteLogin(AppUser user)
{
try
{
await JS.InvokeVoidAsync("localStorage.setItem", "userId", user.Id.ToString());
}
catch { }
UserSession.SetUser(user);
Navigation.NavigateTo("/tire-storage");
}
private void BackToLogin()
{
showTwoFactorStep = false;
twoFactorCode = "";
pendingUser = null;
errorMessage = null;
password = "";
pendingEmailCode = null;
emailCodeExpiry = null;
emailFallbackMessage = null;
}
}