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.
This commit is contained in:
2026-02-02 16:17:53 +01:00
parent abbe6a26a9
commit ada621ac46
2 changed files with 28 additions and 37 deletions

View File

@@ -66,16 +66,7 @@ public class EnvelopeReceiverController : ControllerBase
[HttpGet] [HttpGet]
public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver) public async Task<IActionResult> GetEnvelopeReceiver([FromQuery] ReadEnvelopeReceiverQuery envelopeReceiver)
{ {
var username = User.GetUsernameOrDefault(); envelopeReceiver = envelopeReceiver with { Username = User.GetUsername() };
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 };
var result = await _mediator.Send(envelopeReceiver); var result = await _mediator.Send(envelopeReceiver);

View File

@@ -24,15 +24,6 @@ namespace EnvelopeGenerator.API.Extensions
throw new InvalidOperationException(message); throw new InvalidOperationException(message);
} }
/// <summary>
/// Attempts to retrieve the user's ID from the claims. Returns null if the ID is not found or invalid.
/// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The user's ID as an integer, or null if not found or invalid.</returns>
public static int? GetIdOrDefault(this ClaimsPrincipal user)
=> int.TryParse(user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue("sub"), out int result)
? result : null;
/// <summary> /// <summary>
/// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid. /// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid.
/// </summary> /// </summary>
@@ -40,39 +31,48 @@ namespace EnvelopeGenerator.API.Extensions
/// <returns>The user's ID as an integer.</returns> /// <returns>The user's ID as an integer.</returns>
/// <exception cref="InvalidOperationException">Thrown if the user ID claim is missing or invalid.</exception> /// <exception cref="InvalidOperationException">Thrown if the user ID claim is missing or invalid.</exception>
public static int GetId(this ClaimsPrincipal user) 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;
}
/// <summary> /// <summary>
/// Retrieves the username from the claims, if available. /// Retrieves the username from the claims.
/// </summary> /// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param> /// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The username as a string, or null if not found.</returns> /// <returns>The username as a string.</returns>
public static string? GetUsernameOrDefault(this ClaimsPrincipal user) public static string GetUsername(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Name)?.Value; => user.GetRequiredClaimOfSender(ClaimTypes.Name);
/// <summary> /// <summary>
/// Retrieves the user's surname (last name) from the claims, if available. /// Retrieves the user's surname (last name) from the claims.
/// </summary> /// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param> /// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The surname as a string, or null if not found.</returns> /// <returns>The surname as a string.</returns>
public static string? GetNameOrDefault(this ClaimsPrincipal user) public static string GetName(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Surname)?.Value; => user.GetRequiredClaimOfSender(ClaimTypes.Surname);
/// <summary> /// <summary>
/// Retrieves the user's given name (first name) from the claims, if available. /// Retrieves the user's given name (first name) from the claims.
/// </summary> /// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param> /// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The given name as a string, or null if not found.</returns> /// <returns>The given name as a string.</returns>
public static string? GetPrenameOrDefault(this ClaimsPrincipal user) public static string GetPrename(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.GivenName)?.Value; => user.GetRequiredClaimOfSender(ClaimTypes.GivenName);
/// <summary> /// <summary>
/// Retrieves the user's email address from the claims, if available. /// Retrieves the user's email address from the claims.
/// </summary> /// </summary>
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param> /// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
/// <returns>The email address as a string, or null if not found.</returns> /// <returns>The email address as a string.</returns>
public static string? GetEmailOrDefault(this ClaimsPrincipal user) public static string GetEmail(this ClaimsPrincipal user)
=> user.FindFirst(ClaimTypes.Email)?.Value; => user.GetRequiredClaimOfSender(ClaimTypes.Email);
} }
} }