Fix admin authorization check - properly parse realm_access claim
The realm_access claim in JWT is a JSON object, not a simple string. Previous string contains check was looking for escaped quotes in wrong format. - Parse realm_access as JSON to extract roles array - Check if 'admin' exists in roles array - Fallback to string contains check if JSON parsing fails - Applied fix in RequireGlobalAdmin policy, TenantValidationMiddleware, and ClubRoleClaimsTransformation Fixes: Admin users getting 401 when trying to create clubs
This commit is contained in:
@@ -56,12 +56,35 @@ public class ClubRoleClaimsTransformation : IClaimsTransformation
|
|||||||
|
|
||||||
// --- NEW: Skip DB role lookup if user is a global admin ---
|
// --- NEW: Skip DB role lookup if user is a global admin ---
|
||||||
var realmAccess = principal.FindFirst("realm_access")?.Value;
|
var realmAccess = principal.FindFirst("realm_access")?.Value;
|
||||||
if (!string.IsNullOrEmpty(realmAccess) && realmAccess.Contains("\"admin\"", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(realmAccess) && IsAdminUser(realmAccess))
|
||||||
{
|
{
|
||||||
return Task.FromResult(principal);
|
return Task.FromResult(principal);
|
||||||
}
|
}
|
||||||
// ---------------------------------------------------------
|
// ---------------------------------------------------------
|
||||||
|
|
||||||
|
static bool IsAdminUser(string realmAccess)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = System.Text.Json.JsonDocument.Parse(realmAccess);
|
||||||
|
if (doc.RootElement.TryGetProperty("roles", out var rolesElement) &&
|
||||||
|
rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
foreach (var role in rolesElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
if (role.GetString()?.Equals("admin", StringComparison.OrdinalIgnoreCase) == true)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If JSON parsing fails, fallback to string contains check
|
||||||
|
return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Look up the user's role in the database for the requested tenant
|
// Look up the user's role in the database for the requested tenant
|
||||||
_httpContextAccessor.HttpContext!.Items["TenantId"] = tenantId;
|
_httpContextAccessor.HttpContext!.Items["TenantId"] = tenantId;
|
||||||
var memberRole = GetMemberRole(userIdClaim, tenantId);
|
var memberRole = GetMemberRole(userIdClaim, tenantId);
|
||||||
|
|||||||
@@ -46,12 +46,35 @@ public class TenantValidationMiddleware
|
|||||||
{
|
{
|
||||||
// NEW: Skip check if user is a global admin
|
// NEW: Skip check if user is a global admin
|
||||||
var realmAccess = context.User.FindFirst("realm_access")?.Value;
|
var realmAccess = context.User.FindFirst("realm_access")?.Value;
|
||||||
if (!string.IsNullOrEmpty(realmAccess) && realmAccess.Contains("\"admin\"", StringComparison.OrdinalIgnoreCase))
|
if (!string.IsNullOrEmpty(realmAccess) && IsAdminUser(realmAccess))
|
||||||
{
|
{
|
||||||
await _next(context);
|
await _next(context);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsAdminUser(string realmAccess)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = System.Text.Json.JsonDocument.Parse(realmAccess);
|
||||||
|
if (doc.RootElement.TryGetProperty("roles", out var rolesElement) &&
|
||||||
|
rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
foreach (var role in rolesElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
if (role.GetString()?.Equals("admin", StringComparison.OrdinalIgnoreCase) == true)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If JSON parsing fails, fallback to string contains check
|
||||||
|
return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
await context.Response.WriteAsJsonAsync(new { error = "User does not have clubs claim" });
|
await context.Response.WriteAsJsonAsync(new { error = "User does not have clubs claim" });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -53,7 +53,29 @@ builder.Services.AddAuthorizationBuilder()
|
|||||||
.AddPolicy("RequireGlobalAdmin", policy => policy.RequireAssertion(context =>
|
.AddPolicy("RequireGlobalAdmin", policy => policy.RequireAssertion(context =>
|
||||||
{
|
{
|
||||||
var realmAccess = context.User.FindFirst("realm_access")?.Value;
|
var realmAccess = context.User.FindFirst("realm_access")?.Value;
|
||||||
return realmAccess != null && realmAccess.Contains("\"admin\"");
|
if (string.IsNullOrEmpty(realmAccess))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var doc = System.Text.Json.JsonDocument.Parse(realmAccess);
|
||||||
|
if (doc.RootElement.TryGetProperty("roles", out var rolesElement) &&
|
||||||
|
rolesElement.ValueKind == System.Text.Json.JsonValueKind.Array)
|
||||||
|
{
|
||||||
|
foreach (var role in rolesElement.EnumerateArray())
|
||||||
|
{
|
||||||
|
if (role.GetString() == "admin")
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If JSON parsing fails, fallback to string contains check
|
||||||
|
return realmAccess.Contains("admin", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}))
|
}))
|
||||||
.AddPolicy("RequireManager", policy => policy.RequireRole("Manager"))
|
.AddPolicy("RequireManager", policy => policy.RequireRole("Manager"))
|
||||||
.AddPolicy("RequireMember", policy => policy.RequireRole("Manager", "Member"))
|
.AddPolicy("RequireMember", policy => policy.RequireRole("Manager", "Member"))
|
||||||
|
|||||||
Reference in New Issue
Block a user