Refactor claim handling and simplify controllers

Refactored multiple controllers (`AnnotationController`,
`DocumentController`, `ReadOnlyController`, and
`SignatureController`) to use updated claim extension methods
(`ReceiverSignature`, `EnvelopeUuid`, etc.), replacing older,
verbose methods for improved readability and consistency.

Removed the `EnvelopeClaimTypes` class and replaced claim type
constants with `EnvelopeClaimNames`. Simplified claim retrieval
logic in `ReceiverClaimExtensions` by consolidating methods and
removing redundant or unused functionality.

Eliminated the `SignInEnvelopeAsync` method, indicating a shift
away from manual claim management. Performed general cleanup,
including removing obsolete code and improving exception
messages for better debugging context.
This commit is contained in:
2026-06-09 10:54:38 +02:00
parent 50c02314ef
commit 5b220932d3
6 changed files with 41 additions and 105 deletions

View File

@@ -1,11 +1,9 @@
using DigitalData.Core.Abstraction.Application.DTO; using DigitalData.Core.Abstraction.Application.DTO;
using DigitalData.Core.Exceptions; using DigitalData.Core.Exceptions;
using EnvelopeGenerator.API.Extensions; using EnvelopeGenerator.API.Extensions;
using EnvelopeGenerator.Application.Common.Dto;
using EnvelopeGenerator.Application.Common.Extensions; using EnvelopeGenerator.Application.Common.Extensions;
using EnvelopeGenerator.Application.Common.Interfaces.Services; using EnvelopeGenerator.Application.Common.Interfaces.Services;
using EnvelopeGenerator.Application.Common.Notifications.DocSigned; using EnvelopeGenerator.Application.Common.Notifications.DocSigned;
using EnvelopeGenerator.Application.Documents.Queries;
using EnvelopeGenerator.Application.EnvelopeReceivers.Queries; using EnvelopeGenerator.Application.EnvelopeReceivers.Queries;
using EnvelopeGenerator.Application.Histories.Queries; using EnvelopeGenerator.Application.Histories.Queries;
using EnvelopeGenerator.Domain.Constants; using EnvelopeGenerator.Domain.Constants;
@@ -13,7 +11,6 @@ using MediatR;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace EnvelopeGenerator.API.Controllers; namespace EnvelopeGenerator.API.Controllers;
@@ -62,8 +59,8 @@ public class AnnotationController : ControllerBase
[Obsolete("PSPDF Kit will no longer be used.")] [Obsolete("PSPDF Kit will no longer be used.")]
public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default) public async Task<IActionResult> CreateOrUpdate([FromBody] PsPdfKitAnnotation? psPdfKitAnnotation = null, CancellationToken cancel = default)
{ {
var signature = User.GetReceiverSignatureOfReceiver(); var signature = User.ReceiverSignature();
var uuid = User.GetEnvelopeUuidOfReceiver(); var uuid = User.EnvelopeUuid();
var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound); var envelopeReceiver = await _mediator.ReadEnvelopeReceiverAsync(uuid, signature, cancel).ThrowIfNull(Exceptions.NotFound);
@@ -95,9 +92,9 @@ public class AnnotationController : ControllerBase
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public async Task<IActionResult> Reject([FromBody] string? reason = null) public async Task<IActionResult> Reject([FromBody] string? reason = null)
{ {
var signature = User.GetReceiverSignatureOfReceiver(); var signature = User.ReceiverSignature();
var uuid = User.GetEnvelopeUuidOfReceiver(); var uuid = User.EnvelopeUuid();
var mail = User.GetReceiverMailOfReceiver(); var mail = User.ReceiverMail();
var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature); var envRcvRes = await _envelopeReceiverService.ReadByUuidSignatureAsync(uuid: uuid, signature: signature);

View File

@@ -51,7 +51,7 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
if (query is not null) if (query is not null)
return BadRequest("Query parameters are not allowed for receiver role."); return BadRequest("Query parameters are not allowed for receiver role.");
var envelopeId = User.GetEnvelopeIdOfReceiver(); var envelopeId = User.EnvelopeId();
var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel); var receiverDoc = await mediator.Send(new ReadDocumentQuery { EnvelopeId = envelopeId }, cancel);
return receiverDoc.ByteData is byte[] receiverDocByte return receiverDoc.ByteData is byte[] receiverDocByte
? File(receiverDocByte, "application/octet-stream") ? File(receiverDocByte, "application/octet-stream")
@@ -71,7 +71,7 @@ public class DocumentController(IMediator mediator, IAuthorizationService authSe
[HttpGet("{envelopeKey}")] [HttpGet("{envelopeKey}")]
public async Task<IActionResult> GetDocumentOfReceiver(string envelopeKey, CancellationToken cancel) public async Task<IActionResult> GetDocumentOfReceiver(string envelopeKey, CancellationToken cancel)
{ {
int envelopeId = User.GetEnvelopeIdOfReceiver(); int envelopeId = User.EnvelopeId();
var senderDoc = await mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel); var senderDoc = await mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);

View File

@@ -41,14 +41,14 @@ public class ReadOnlyController : ControllerBase
[Obsolete("Use MediatR")] [Obsolete("Use MediatR")]
public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto) public async Task<IActionResult> CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
{ {
var authReceiverMail = User.GetReceiverMailOfReceiver(); var authReceiverMail = User.ReceiverMail();
if (authReceiverMail is null) if (authReceiverMail is null)
{ {
_logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto)); _logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
return Unauthorized(); return Unauthorized();
} }
var envelopeId = User.GetEnvelopeIdOfReceiver(); var envelopeId = User.EnvelopeId();
createDto.AddedWho = authReceiverMail; createDto.AddedWho = authReceiverMail;
createDto.EnvelopeId = envelopeId; createDto.EnvelopeId = envelopeId;

View File

@@ -38,9 +38,9 @@ public class SignatureController : ControllerBase
[HttpGet("{envelopeKey}")] [HttpGet("{envelopeKey}")]
public async Task<IActionResult> GetAnnotsOfReceiver(string envelopeKey, CancellationToken cancel) public async Task<IActionResult> GetAnnotsOfReceiver(string envelopeKey, CancellationToken cancel)
{ {
int envelopeId = User.GetEnvelopeIdOfReceiver(); int envelopeId = User.EnvelopeId();
int receiverId = User.GetReceiverIdOfReceiver(); int receiverId = User.ReceiverId();
var doc = await _mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel); var doc = await _mediator.Send(new ReadDocumentQuery() { EnvelopeId = envelopeId }, cancel);

View File

@@ -1,17 +0,0 @@
namespace EnvelopeGenerator.API;
/// <summary>
/// Provides custom claim types for envelope-related information.
/// </summary>
public static class EnvelopeClaimTypes
{
/// <summary>
/// Claim type for the title of an envelope.
/// </summary>
public static readonly string Title = $"Envelope{nameof(Title)}";
/// <summary>
/// Claim type for the ID of an envelope.
/// </summary>
public static readonly string Id = $"Envelope{nameof(Id)}";
}

View File

@@ -1,8 +1,6 @@
using System.Linq; using DigitalData.Auth.Claims;
using Microsoft.IdentityModel.JsonWebTokens;
using System.Security.Claims; using System.Security.Claims;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace EnvelopeGenerator.API.Extensions; namespace EnvelopeGenerator.API.Extensions;
@@ -11,12 +9,14 @@ namespace EnvelopeGenerator.API.Extensions;
/// </summary> /// </summary>
public static class ReceiverClaimExtensions public static class ReceiverClaimExtensions
{ {
private static readonly string[] EnvelopeIdClaimTypes = [EnvelopeClaimTypes.Id, "envelope_id", "EnvelopeId"]; /// <summary>
private static readonly string[] ReceiverIdClaimTypes = ["receiver_id", "ReceiverId"]; ///
private static readonly string[] EnvelopeUuidClaimTypes = [ClaimTypes.NameIdentifier, "envelope_uuid", "EnvelopeUuid"]; /// </summary>
private static readonly string[] ReceiverSignatureClaimTypes = [ClaimTypes.Hash, "receiver_sig", "ReceiverSignature"]; /// <param name="user"></param>
/// <param name="claimType"></param>
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, string claimType) /// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
private static string GetRequiredClaimValue(this ClaimsPrincipal user, string claimType)
{ {
var value = user.FindFirstValue(claimType); var value = user.FindFirstValue(claimType);
if (value is not null) if (value is not null)
@@ -32,7 +32,7 @@ public static class ReceiverClaimExtensions
throw new InvalidOperationException(message); throw new InvalidOperationException(message);
} }
private static string GetRequiredClaimOfReceiver(this ClaimsPrincipal user, params string[] claimTypes) private static string GetRequiredClaimValue(this ClaimsPrincipal user, params string[] claimTypes)
{ {
foreach (var claimType in claimTypes.Where(t => !string.IsNullOrWhiteSpace(t)).Distinct()) foreach (var claimType in claimTypes.Where(t => !string.IsNullOrWhiteSpace(t)).Distinct())
{ {
@@ -52,89 +52,45 @@ public static class ReceiverClaimExtensions
/// <summary> /// <summary>
/// Gets the authenticated envelope UUID from the claims. /// Gets the authenticated envelope UUID from the claims.
/// </summary> /// </summary>
public static string GetEnvelopeUuidOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeUuidClaimTypes); public static string EnvelopeUuid(this ClaimsPrincipal user)
=> user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeUuid);
/// <summary> /// <summary>
/// Gets the authenticated receiver signature from the claims. /// Gets the authenticated receiver signature from the claims.
/// </summary> /// </summary>
public static string GetReceiverSignatureOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ReceiverSignatureClaimTypes); public static string ReceiverSignature(this ClaimsPrincipal user)
=> user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverSignature);
/// <summary>
/// Gets the authenticated receiver display name from the claims.
/// </summary>
public static string GetReceiverNameOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Name);
/// <summary> /// <summary>
/// Gets the authenticated receiver email address from the claims. /// Gets the authenticated receiver email address from the claims.
/// </summary> /// </summary>
public static string GetReceiverMailOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(ClaimTypes.Email); public static string ReceiverMail(this ClaimsPrincipal user)
=> user.GetRequiredClaimValue(JwtRegisteredClaimNames.Email);
/// <summary>
/// Gets the authenticated envelope title from the claims.
/// </summary>
public static string GetEnvelopeTitleOfReceiver(this ClaimsPrincipal user) => user.GetRequiredClaimOfReceiver(EnvelopeClaimTypes.Title);
/// <summary> /// <summary>
/// Gets the authenticated envelope identifier from the claims. /// Gets the authenticated envelope identifier from the claims.
/// </summary> /// </summary>
public static int GetEnvelopeIdOfReceiver(this ClaimsPrincipal user) public static int EnvelopeId(this ClaimsPrincipal user)
{ {
var envIdStr = user.GetRequiredClaimOfReceiver(EnvelopeIdClaimTypes); var envIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.EnvelopeId);
if (!int.TryParse(envIdStr, out var envId)) if (int.TryParse(envIdStr, out var envId))
{ return envId;
throw new InvalidOperationException($"Claim '{"envelope_id"}' is not a valid integer."); else
} throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.EnvelopeId}' is not a valid integer.");
return envId;
} }
/// <summary> /// <summary>
/// /// Gets the authenticated receiver identifier from the claims.
/// </summary> /// </summary>
/// <param name="user"></param> /// <param name="user"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="InvalidOperationException"></exception> /// <exception cref="InvalidOperationException"></exception>
public static int GetReceiverIdOfReceiver(this ClaimsPrincipal user) public static int ReceiverId(this ClaimsPrincipal user)
{ {
var rcvIdStr = user.GetRequiredClaimOfReceiver(ReceiverIdClaimTypes); var rcvIdStr = user.GetRequiredClaimValue(EnvelopeClaimNames.ReceiverId);
if (!int.TryParse(rcvIdStr, out var rcvId)) if (int.TryParse(rcvIdStr, out var rcvId))
{ return rcvId;
throw new InvalidOperationException($"Claim '{"receiver_id"}' is not a valid integer."); else
} throw new InvalidOperationException($"Claim '{EnvelopeClaimNames.ReceiverId}' is not a valid integer.");
return rcvId;
}
/// <summary>
/// Signs in an envelope receiver using cookie authentication and attaches envelope claims.
/// </summary>
/// <param name="context">The current HTTP context.</param>
/// <param name="envelopeReceiver">Envelope receiver DTO to extract claims from.</param>
/// <param name="receiverRole">Role to attach to the authentication ticket.</param>
public static async Task SignInEnvelopeAsync(this HttpContext context, EnvelopeReceiverDto envelopeReceiver, string receiverRole)
{
var claims = new List<Claim>
{
new(ClaimTypes.NameIdentifier, envelopeReceiver.Envelope!.Uuid),
new(ClaimTypes.Hash, envelopeReceiver.Receiver!.Signature),
new(ClaimTypes.Name, envelopeReceiver.Name ?? string.Empty),
new(ClaimTypes.Email, envelopeReceiver.Receiver.EmailAddress),
new(EnvelopeClaimTypes.Title, envelopeReceiver.Envelope.Title),
new(EnvelopeClaimTypes.Id, envelopeReceiver.Envelope.Id.ToString()),
new(ClaimTypes.Role, receiverRole)
};
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
AllowRefresh = false,
IsPersistent = false
};
await context.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
} }
} }