diff --git a/src/Meezi.API/Controllers/AuthController.cs b/src/Meezi.API/Controllers/AuthController.cs index 9d9dd00..002aeef 100644 --- a/src/Meezi.API/Controllers/AuthController.cs +++ b/src/Meezi.API/Controllers/AuthController.cs @@ -198,7 +198,10 @@ public class AuthController : ControllerBase ExpiresAt: expiresAt, UserId: userId, CafeId: User.FindFirstValue(MeeziClaimTypes.CafeId) ?? string.Empty, - Role: User.FindFirstValue(MeeziClaimTypes.Role) ?? string.Empty, + // .NET remaps the short "role" claim to ClaimTypes.Role on inbound; read both. + Role: User.FindFirstValue(MeeziClaimTypes.Role) + ?? User.FindFirstValue(System.Security.Claims.ClaimTypes.Role) + ?? string.Empty, PlanTier: User.FindFirstValue(MeeziClaimTypes.PlanTier) ?? string.Empty, Language: User.FindFirstValue(MeeziClaimTypes.Language) ?? string.Empty, Actor: User.FindFirstValue(MeeziClaimTypes.Actor) ?? MeeziActorKinds.Merchant, diff --git a/src/Meezi.API/Middleware/TenantMiddleware.cs b/src/Meezi.API/Middleware/TenantMiddleware.cs index 5dcd636..8c28526 100644 --- a/src/Meezi.API/Middleware/TenantMiddleware.cs +++ b/src/Meezi.API/Middleware/TenantMiddleware.cs @@ -92,7 +92,12 @@ public class TenantMiddleware { scopedMerchant.CafeId = cafeId; - var roleClaim = context.User.FindFirst(MeeziClaimTypes.Role)?.Value; + // .NET's JWT handler remaps the short "role" claim to ClaimTypes.Role + // on inbound, so FindFirst("role") returns null and tenant.Role would + // stay null — making EnsureManager/EnsureOwner reject even a real owner. + // Read both the raw claim and the mapped one. + var roleClaim = context.User.FindFirst(MeeziClaimTypes.Role)?.Value + ?? context.User.FindFirst(System.Security.Claims.ClaimTypes.Role)?.Value; if (Enum.TryParse(roleClaim, ignoreCase: true, out var role)) scopedMerchant.Role = role;