From ada621ac4696f2ba074fbd8d3af118921865c344 Mon Sep 17 00:00:00 2001 From: TekH Date: Mon, 2 Feb 2026 16:17:53 +0100 Subject: [PATCH] Refactor claim access to enforce required user claims Replaced nullable claim accessors with strict versions that throw exceptions if required claims are missing or invalid. Updated controller logic to use new methods and removed fallback/error handling for missing claims, ensuring stricter claim validation throughout the codebase. --- .../Controllers/EnvelopeReceiverController.cs | 11 +--- .../Extensions/SenderClaimExtensions.cs | 54 +++++++++---------- 2 files changed, 28 insertions(+), 37 deletions(-) diff --git a/EnvelopeGenerator.API/Controllers/EnvelopeReceiverController.cs b/EnvelopeGenerator.API/Controllers/EnvelopeReceiverController.cs index e8125779..db8371ee 100644 --- a/EnvelopeGenerator.API/Controllers/EnvelopeReceiverController.cs +++ b/EnvelopeGenerator.API/Controllers/EnvelopeReceiverController.cs @@ -66,16 +66,7 @@ public class EnvelopeReceiverController : ControllerBase [HttpGet] public async Task GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver) { - var username = User.GetUsernameOrDefault(); - - if (username is null) - { - _logger.LogError(@"Envelope Receiver dto cannot be sent because username claim is null. Potential authentication and authorization error. The value of other claims are [id: {id}], [username: {username}], [name: {name}], [prename: {prename}], [email: {email}].", - User.GetId(), User.GetUsernameOrDefault(), User.GetNameOrDefault(), User.GetPrenameOrDefault(), User.GetEmailOrDefault()); - return StatusCode(StatusCodes.Status500InternalServerError); - } - - envelopeReceiver = envelopeReceiver with { Username = username }; + envelopeReceiver = envelopeReceiver with { Username = User.GetUsername() }; var result = await _mediator.Send(envelopeReceiver); diff --git a/EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs b/EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs index 9284aca4..613d9157 100644 --- a/EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs +++ b/EnvelopeGenerator.API/Extensions/SenderClaimExtensions.cs @@ -24,15 +24,6 @@ namespace EnvelopeGenerator.API.Extensions throw new InvalidOperationException(message); } - /// - /// Attempts to retrieve the user's ID from the claims. Returns null if the ID is not found or invalid. - /// - /// The representing the user. - /// The user's ID as an integer, or null if not found or invalid. - public static int? GetIdOrDefault(this ClaimsPrincipal user) - => int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result) - ? result : null; - /// /// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid. /// @@ -40,39 +31,48 @@ namespace EnvelopeGenerator.API.Extensions /// The user's ID as an integer. /// Thrown if the user ID claim is missing or invalid. public static int GetId(this ClaimsPrincipal user) - => user.GetIdOrDefault() - ?? throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token."); + { + var idValue = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"); + idValue ??= user.GetRequiredClaimOfSender(ClaimTypes.NameIdentifier); + + if (!int.TryParse(idValue, out var result)) + { + throw new InvalidOperationException("User ID claim is missing or invalid. This may indicate a misconfigured or forged JWT token."); + } + + return result; + } /// - /// Retrieves the username from the claims, if available. + /// Retrieves the username from the claims. /// /// The representing the user. - /// The username as a string, or null if not found. - public static string? GetUsernameOrDefault(this ClaimsPrincipal user) - => user.FindFirst(ClaimTypes.Name)?.Value; + /// The username as a string. + public static string GetUsername(this ClaimsPrincipal user) + => user.GetRequiredClaimOfSender(ClaimTypes.Name); /// - /// Retrieves the user's surname (last name) from the claims, if available. + /// Retrieves the user's surname (last name) from the claims. /// /// The representing the user. - /// The surname as a string, or null if not found. - public static string? GetNameOrDefault(this ClaimsPrincipal user) - => user.FindFirst(ClaimTypes.Surname)?.Value; + /// The surname as a string. + public static string GetName(this ClaimsPrincipal user) + => user.GetRequiredClaimOfSender(ClaimTypes.Surname); /// - /// Retrieves the user's given name (first name) from the claims, if available. + /// Retrieves the user's given name (first name) from the claims. /// /// The representing the user. - /// The given name as a string, or null if not found. - public static string? GetPrenameOrDefault(this ClaimsPrincipal user) - => user.FindFirst(ClaimTypes.GivenName)?.Value; + /// The given name as a string. + public static string GetPrename(this ClaimsPrincipal user) + => user.GetRequiredClaimOfSender(ClaimTypes.GivenName); /// - /// Retrieves the user's email address from the claims, if available. + /// Retrieves the user's email address from the claims. /// /// The representing the user. - /// The email address as a string, or null if not found. - public static string? GetEmailOrDefault(this ClaimsPrincipal user) - => user.FindFirst(ClaimTypes.Email)?.Value; + /// The email address as a string. + public static string GetEmail(this ClaimsPrincipal user) + => user.GetRequiredClaimOfSender(ClaimTypes.Email); } } \ No newline at end of file