shoc-frontend-new/src/lib/auth/org-scope.ts
Arthur Bassi 4f16b6a76f fix(work-orders): trust only JWT org_scope for account list
Ignore stored AuthUser.orgScope so a scoped token cannot open
the unscoped account picker via localStorage.
2026-08-25 21:31:18 -03:00

44 lines
1.3 KiB
TypeScript

import type { AuthUser } from "@/domain/auth/types/auth-user";
const ORG_SCOPE_ALL = "all";
function readJwtClaims(token: string): Record<string, unknown> | null {
const parts = token.split(".");
if (parts.length < 2) {
return null;
}
try {
const normalized = parts[1].replace(/-/g, "+").replace(/_/g, "/");
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, "=");
const parsed: unknown = JSON.parse(globalThis.atob(padded));
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
return null;
}
return parsed as Record<string, unknown>;
} catch {
return null;
}
}
function readStringClaim(source: Record<string, unknown> | null, keys: string[]): string {
if (!source) {
return "";
}
for (const key of keys) {
const value = source[key];
if (typeof value === "string" && value.trim()) {
return value.trim();
}
}
return "";
}
/** Org-wide only when the JWT claim is explicitly `all`. Fail-closed otherwise. */
export function isOrgWideScope(user: AuthUser | null | undefined): boolean {
if (!user || typeof user.token !== "string") {
return false;
}
const claims = readJwtClaims(user.token);
const scope = readStringClaim(claims, ["org_scope", "orgScope", "OrgScope"]);
return scope.toLowerCase() === ORG_SCOPE_ALL;
}