using Microsoft.AspNetCore.Mvc; using System.Security.Claims; namespace DorkNet.Server.Auth; /// /// Single canonical place for "who is the bearer-resolved player on this /// request." Replaces the per-controller copy-paste of: /// /// private long CurrentPlayerId => /// long.Parse(User.FindFirstValue(ClaimTypes.NameIdentifier)!); /// /// which historically existed in 10 controllers and silently NPE-bombed /// the action when the bearer was missing instead of returning 401. /// public static class ControllerBaseExtensions { /// /// Returns the bearer-resolved player id, or null when the caller is /// anonymous / the bearer is malformed. Use on optional-auth endpoints. /// public static long? CurrentPlayerId(this ControllerBase controller) => CurrentPlayerId(controller.User); /// Overload for non-controller callers (filters, /// middleware) that hold a directly. public static long? CurrentPlayerId(ClaimsPrincipal user) { var claim = user.FindFirstValue(ClaimTypes.NameIdentifier); return long.TryParse(claim, out var id) ? id : null; } /// /// Returns the bearer-resolved player id; throws /// when missing. Use on /// [Authorize]-decorated endpoints where a missing id is a /// programming error (the framework should have already 401'd). /// public static long RequireCurrentPlayerId(this ControllerBase controller) => controller.CurrentPlayerId() ?? throw new UnauthorizedAccessException( "Bearer token claim 'NameIdentifier' is missing or non-numeric. " + "If this fired on an [Authorize] endpoint, JWT validation upstream " + "is misconfigured."); }