Add authentication for secure document access
Introduced a new login page (`Login.razor`) to handle user authentication via access codes. Implemented `AuthService` to validate access codes through an API. Updated `ReportViewer.razor` to check user access and redirect unauthorized users to the login page. Modified `Program.cs` to register `AuthService` for dependency injection. Enhanced document fetching in `ReportViewer.razor` to ensure secure access control.
This commit is contained in:
71
EnvelopeGenerator.ReceiverUI/Pages/Login.razor
Normal file
71
EnvelopeGenerator.ReceiverUI/Pages/Login.razor
Normal file
@@ -0,0 +1,71 @@
|
||||
@page "/login/{EnvelopeKey}"
|
||||
@using EnvelopeGenerator.ReceiverUI.Options
|
||||
@using Microsoft.Extensions.Options
|
||||
|
||||
<div class="login-page-wrapper d-flex align-items-center justify-content-center min-vh-100 bg-light">
|
||||
<div class="card shadow-sm" style="max-width: 420px; width: 100%;">
|
||||
<div class="card-body p-4">
|
||||
|
||||
<h4 class="card-title mb-1">Zugang zum Dokument</h4>
|
||||
<p class="text-muted mb-4" style="font-size: 0.9rem;">
|
||||
Bitte geben Sie den Zugangscode ein, den Sie per E-Mail erhalten haben, um das Dokument zu öffnen.
|
||||
</p>
|
||||
|
||||
@if (!string.IsNullOrWhiteSpace(ErrorMessage)) {
|
||||
<div class="alert alert-danger py-2">@ErrorMessage</div>
|
||||
}
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="login-access-code">Zugangscode</label>
|
||||
<input id="login-access-code"
|
||||
type="password"
|
||||
class="form-control"
|
||||
placeholder="Zugangscode eingeben"
|
||||
@bind="AccessCode"
|
||||
@bind:event="oninput"
|
||||
@onkeydown="OnKeyDownAsync"
|
||||
disabled="@IsLoading" />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary w-100"
|
||||
@onclick="SubmitAsync"
|
||||
disabled="@(IsLoading || string.IsNullOrWhiteSpace(AccessCode))">
|
||||
@if (IsLoading) {
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
<span>Überprüfen …</span>
|
||||
} else {
|
||||
<span>Weiter</span>
|
||||
}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
[Parameter] public string EnvelopeKey { get; set; } = string.Empty;
|
||||
|
||||
string AccessCode = string.Empty;
|
||||
string? ErrorMessage;
|
||||
bool IsLoading;
|
||||
|
||||
async Task OnKeyDownAsync(Microsoft.AspNetCore.Components.Web.KeyboardEventArgs e) {
|
||||
if (e.Key == "Enter")
|
||||
await SubmitAsync();
|
||||
}
|
||||
|
||||
async Task SubmitAsync() {
|
||||
if (string.IsNullOrWhiteSpace(AccessCode)) return;
|
||||
|
||||
IsLoading = true;
|
||||
ErrorMessage = null;
|
||||
await InvokeAsync(StateHasChanged);
|
||||
|
||||
// TODO: API entegrasyonu buraya eklenecek
|
||||
await Task.Delay(500); // placeholder
|
||||
|
||||
IsLoading = false;
|
||||
ErrorMessage = "Der eingegebene Zugangscode ist ungültig. Bitte versuchen Sie es erneut.";
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,10 @@
|
||||
@using EnvelopeGenerator.ReceiverUI.Options
|
||||
@inject IJSRuntime JSRuntime
|
||||
@inject IOptions<ApiOptions> AppOptions
|
||||
@inject NavigationManager Navigation
|
||||
@inject InMemoryReportStorageWebExtension ReportStorage
|
||||
@inject EnvelopeGenerator.ReceiverUI.Services.DocumentService DocumentService
|
||||
@inject EnvelopeGenerator.ReceiverUI.Services.AuthService AuthService
|
||||
|
||||
<link href="_content/DevExpress.Blazor.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
||||
<link href="_content/DevExpress.Blazor.Reporting.Viewer/css/dx-blazor-reporting-components.bs5.css" rel="stylesheet" />
|
||||
@@ -170,6 +172,14 @@
|
||||
|
||||
protected override async Task OnInitializedAsync() {
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(EnvelopeKey)) {
|
||||
var hasAccess = await AuthService.CheckEnvelopeAccessAsync(EnvelopeKey);
|
||||
if (!hasAccess) {
|
||||
Navigation.NavigateTo($"/login/{Uri.EscapeDataString(EnvelopeKey)}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!AppOptions.Value.ForceToUseFakeDocument && !string.IsNullOrWhiteSpace(EnvelopeKey)) {
|
||||
var (pdfBytes, _) = await DocumentService.GetDocumentAsync(EnvelopeKey);
|
||||
if (pdfBytes is { Length: > 0 }) {
|
||||
|
||||
@@ -15,6 +15,7 @@ builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.
|
||||
builder.Services.Configure<ApiOptions>(opts =>
|
||||
builder.Configuration.GetSection(ApiOptions.SectionName).Bind(opts));
|
||||
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.DocumentService>();
|
||||
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.AuthService>();
|
||||
|
||||
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();
|
||||
builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer();
|
||||
|
||||
20
EnvelopeGenerator.ReceiverUI/Services/AuthService.cs
Normal file
20
EnvelopeGenerator.ReceiverUI/Services/AuthService.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Net;
|
||||
using EnvelopeGenerator.ReceiverUI.Options;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace EnvelopeGenerator.ReceiverUI.Services;
|
||||
|
||||
public class AuthService(HttpClient http, IOptions<ApiOptions> apiOptions)
|
||||
{
|
||||
private readonly ApiOptions _api = apiOptions.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the current user holds a valid receiver token for the given envelope key.
|
||||
/// Calls GET /api/auth/check/envelope/{envelopeKey}.
|
||||
/// </summary>
|
||||
public async Task<bool> CheckEnvelopeAccessAsync(string envelopeKey, CancellationToken cancel = default)
|
||||
{
|
||||
var response = await http.GetAsync($"{_api.BaseUrl}/api/auth/check/envelope/{Uri.EscapeDataString(envelopeKey)}", cancel);
|
||||
return response.StatusCode == HttpStatusCode.OK;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user