Files
EnvelopeGenerator/EnvelopeGenerator.API/Extensions/EnvelopeAuthExtensions.cs
TekH f995fa9fc3 Refactor claim accessors to enforce required claims
Refactored EnvelopeAuthExtensions to require presence of all key authentication claims. Added GetRequiredClaim helper that throws detailed exceptions if claims are missing or invalid, replacing nullable return types with non-nullable ones. This ensures authentication logic fails fast and provides clearer error messages when claims are misconfigured or tampered with.
2026-02-02 14:54:59 +01:00

109 lines
4.5 KiB
C#

using System.Linq;
using System.Security.Claims;
using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiver;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
namespace EnvelopeGenerator.API.Extensions;
/// <summary>
/// Provides helper methods for working with envelope-specific authentication claims.
/// </summary>
public static class EnvelopeAuthExtensions
{
private static string GetRequiredClaim(this ClaimsPrincipal user, string claimType)
{
var value = user.FindFirstValue(claimType);
if (value is not null)
{
return value;
}
var identity = user.Identity;
var principalName = identity?.Name ?? "(anonymous)";
var authType = identity?.AuthenticationType ?? "(none)";
var availableClaims = string.Join(", ", user.Claims.Select(c => $"{c.Type}={c.Value}"));
var message = $"Required claim '{claimType}' is missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
throw new InvalidOperationException(message);
}
/// <summary>
/// Retrieves a claim value by type.
/// </summary>
/// <param name="user">The current claims principal.</param>
/// <param name="claimType">The claim type to resolve.</param>
/// <returns>The claim value.</returns>
public static string GetClaimValue(this ClaimsPrincipal user, string claimType) => user.GetRequiredClaim(claimType);
/// <summary>
/// Gets the authenticated envelope UUID from the claims.
/// </summary>
public static string GetAuthEnvelopeUuid(this ClaimsPrincipal user) => user.GetRequiredClaim(ClaimTypes.NameIdentifier);
/// <summary>
/// Gets the authenticated receiver signature from the claims.
/// </summary>
public static string GetAuthReceiverSignature(this ClaimsPrincipal user) => user.GetRequiredClaim(ClaimTypes.Hash);
/// <summary>
/// Gets the authenticated receiver display name from the claims.
/// </summary>
public static string GetAuthReceiverName(this ClaimsPrincipal user) => user.GetRequiredClaim(ClaimTypes.Name);
/// <summary>
/// Gets the authenticated receiver email address from the claims.
/// </summary>
public static string GetAuthReceiverMail(this ClaimsPrincipal user) => user.GetRequiredClaim(ClaimTypes.Email);
/// <summary>
/// Gets the authenticated envelope title from the claims.
/// </summary>
public static string GetAuthEnvelopeTitle(this ClaimsPrincipal user) => user.GetRequiredClaim(EnvelopeClaimTypes.Title);
/// <summary>
/// Gets the authenticated envelope identifier from the claims.
/// </summary>
public static int GetAuthEnvelopeId(this ClaimsPrincipal user)
{
var envIdStr = user.GetRequiredClaim(EnvelopeClaimTypes.Id);
if (!int.TryParse(envIdStr, out var envId))
{
throw new InvalidOperationException($"Claim '{EnvelopeClaimTypes.Id}' is not a valid integer.");
}
return envId;
}
/// <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);
}
}