Added a private extension method GetRequiredClaimOfSender to ClaimsPrincipal for retrieving the first available value from multiple claim types, throwing a detailed exception if none are found. Refactored GetId to use this method, improving code reuse and clarity when handling user claims.
95 lines
4.3 KiB
C#
95 lines
4.3 KiB
C#
using System.Security.Claims;
|
|
|
|
namespace EnvelopeGenerator.API.Extensions
|
|
{
|
|
/// <summary>
|
|
/// Provides extension methods for extracting user information from a <see cref="ClaimsPrincipal"/>.
|
|
/// </summary>
|
|
public static class SenderClaimExtensions
|
|
{
|
|
private static string GetRequiredClaimOfSender(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);
|
|
}
|
|
|
|
private static string GetRequiredClaimOfSender(this ClaimsPrincipal user, params string[] claimTypes)
|
|
{
|
|
string? value = null;
|
|
|
|
foreach (var claimType in claimTypes)
|
|
{
|
|
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 among [{string.Join(", ", claimTypes)}] is missing for user '{principalName}' (auth: {authType}). Available claims: [{availableClaims}].";
|
|
throw new InvalidOperationException(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves the user's ID from the claims. Throws an exception if the ID is missing or invalid.
|
|
/// </summary>
|
|
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
/// <returns>The user's ID as an integer.</returns>
|
|
/// <exception cref="InvalidOperationException">Thrown if the user ID claim is missing or invalid.</exception>
|
|
public static int GetId(this ClaimsPrincipal user)
|
|
{
|
|
var idValue = user.GetRequiredClaimOfSender(ClaimTypes.NameIdentifier, "sub");
|
|
|
|
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>
|
|
/// Retrieves the username from the claims.
|
|
/// </summary>
|
|
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
/// <returns>The username as a string.</returns>
|
|
public static string GetUsername(this ClaimsPrincipal user)
|
|
=> user.GetRequiredClaimOfSender(ClaimTypes.Name);
|
|
|
|
/// <summary>
|
|
/// Retrieves the user's surname (last name) from the claims.
|
|
/// </summary>
|
|
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
/// <returns>The surname as a string.</returns>
|
|
public static string GetName(this ClaimsPrincipal user)
|
|
=> user.GetRequiredClaimOfSender(ClaimTypes.Surname);
|
|
|
|
/// <summary>
|
|
/// Retrieves the user's given name (first name) from the claims.
|
|
/// </summary>
|
|
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
/// <returns>The given name as a string.</returns>
|
|
public static string GetPrename(this ClaimsPrincipal user)
|
|
=> user.GetRequiredClaimOfSender(ClaimTypes.GivenName);
|
|
|
|
/// <summary>
|
|
/// Retrieves the user's email address from the claims.
|
|
/// </summary>
|
|
/// <param name="user">The <see cref="ClaimsPrincipal"/> representing the user.</param>
|
|
/// <returns>The email address as a string.</returns>
|
|
public static string GetEmail(this ClaimsPrincipal user)
|
|
=> user.GetRequiredClaimOfSender(ClaimTypes.Email);
|
|
}
|
|
} |