111 lines
2.8 KiB
Plaintext
111 lines
2.8 KiB
Plaintext
@page "/login"
|
|
@rendermode InteractiveServer
|
|
@attribute [AllowAnonymous]
|
|
@inject IJSRuntime JS
|
|
@inject NavigationManager Nav
|
|
@inject AuthenticationStateProvider AuthStateProvider
|
|
|
|
<PageTitle>Sign In — Chrono CCG</PageTitle>
|
|
|
|
<div class="login-container">
|
|
<div class="login-card">
|
|
<h1 class="login-title">Chrono CCG</h1>
|
|
|
|
@if (_error is not null)
|
|
{
|
|
<div class="alert alert-danger">@_error</div>
|
|
}
|
|
|
|
@if (_enrolled)
|
|
{
|
|
<p class="login-hint">Use your registered passkey to sign in.</p>
|
|
<button class="btn btn-primary btn-lg w-100" @onclick="SignIn" disabled="@_busy">
|
|
@if (_busy)
|
|
{
|
|
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
}
|
|
Sign in with Passkey
|
|
</button>
|
|
}
|
|
else
|
|
{
|
|
<p class="login-hint">No passkey enrolled yet. Register your device to get started.</p>
|
|
<button class="btn btn-success btn-lg w-100" @onclick="Enroll" disabled="@_busy">
|
|
@if (_busy)
|
|
{
|
|
<span class="spinner-border spinner-border-sm me-2" role="status"></span>
|
|
}
|
|
Enroll Passkey
|
|
</button>
|
|
}
|
|
</div>
|
|
</div>
|
|
|
|
@code {
|
|
private bool _enrolled;
|
|
private bool _busy;
|
|
private string? _error;
|
|
|
|
protected override async Task OnInitializedAsync()
|
|
{
|
|
var authState = await AuthStateProvider.GetAuthenticationStateAsync();
|
|
if (authState.User.Identity?.IsAuthenticated == true)
|
|
{
|
|
Nav.NavigateTo("/", replace: true);
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
using var http = new HttpClient { BaseAddress = new Uri(Nav.BaseUri) };
|
|
var status = await http.GetFromJsonAsync<StatusResponse>("/api/auth/status");
|
|
_enrolled = status?.Enrolled ?? false;
|
|
}
|
|
catch
|
|
{
|
|
_enrolled = false;
|
|
}
|
|
}
|
|
|
|
private async Task SignIn()
|
|
{
|
|
_busy = true;
|
|
_error = null;
|
|
try
|
|
{
|
|
await JS.InvokeVoidAsync("passkeyLogin");
|
|
Nav.NavigateTo("/", true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_error = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
_busy = false;
|
|
}
|
|
}
|
|
|
|
private async Task Enroll()
|
|
{
|
|
_busy = true;
|
|
_error = null;
|
|
try
|
|
{
|
|
await JS.InvokeVoidAsync("passkeyEnroll");
|
|
Nav.NavigateTo("/", true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_error = ex.Message;
|
|
}
|
|
finally
|
|
{
|
|
_busy = false;
|
|
}
|
|
}
|
|
|
|
private record StatusResponse(bool Enrolled, bool Authenticated);
|
|
|
|
}
|