mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-05 13:02:06 +00:00
46 lines
1.4 KiB
TypeScript
46 lines
1.4 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 authenticated scope is explicitly `all`. Fail-closed otherwise. */
|
||
|
|
export function isOrgWideScope(user: AuthUser | null | undefined): boolean {
|
||
|
|
if (!user) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
const claims = typeof user.token === "string" ? readJwtClaims(user.token) : null;
|
||
|
|
const fromJwt = readStringClaim(claims, ["org_scope", "orgScope", "OrgScope"]);
|
||
|
|
const fromUser = typeof user.orgScope === "string" ? user.orgScope.trim() : "";
|
||
|
|
return (fromJwt || fromUser).toLowerCase() === ORG_SCOPE_ALL;
|
||
|
|
}
|