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
|
@using EnvelopeGenerator.ReceiverUI.Options
|
||||||
@inject IJSRuntime JSRuntime
|
@inject IJSRuntime JSRuntime
|
||||||
@inject IOptions<ApiOptions> AppOptions
|
@inject IOptions<ApiOptions> AppOptions
|
||||||
|
@inject NavigationManager Navigation
|
||||||
@inject InMemoryReportStorageWebExtension ReportStorage
|
@inject InMemoryReportStorageWebExtension ReportStorage
|
||||||
@inject EnvelopeGenerator.ReceiverUI.Services.DocumentService DocumentService
|
@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.Themes/blazing-berry.bs5.min.css" rel="stylesheet" />
|
||||||
<link href="_content/DevExpress.Blazor.Reporting.Viewer/css/dx-blazor-reporting-components.bs5.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() {
|
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)) {
|
if (!AppOptions.Value.ForceToUseFakeDocument && !string.IsNullOrWhiteSpace(EnvelopeKey)) {
|
||||||
var (pdfBytes, _) = await DocumentService.GetDocumentAsync(EnvelopeKey);
|
var (pdfBytes, _) = await DocumentService.GetDocumentAsync(EnvelopeKey);
|
||||||
if (pdfBytes is { Length: > 0 }) {
|
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.Services.Configure<ApiOptions>(opts =>
|
||||||
builder.Configuration.GetSection(ApiOptions.SectionName).Bind(opts));
|
builder.Configuration.GetSection(ApiOptions.SectionName).Bind(opts));
|
||||||
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.DocumentService>();
|
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.DocumentService>();
|
||||||
|
builder.Services.AddScoped<EnvelopeGenerator.ReceiverUI.Services.AuthService>();
|
||||||
|
|
||||||
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();
|
builder.Services.AddDevExpressWebAssemblyBlazorReportViewer();
|
||||||
builder.Services.AddDevExpressWebAssemblyBlazorPdfViewer();
|
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