Completes the arcadia-admin operator surface for the integration registry and the capability/route-guard framework it depends on. - Integration registry: route + Data-group nav entry + `platform.integrations` capability; the in-app client now delegates to the shared `@crema/integration-registry-client` lib (vite alias + tsconfig); the operator Integrations page (committed earlier) is now reachable. - Capability gating: capabilities map + route-guard + jwt helpers + the apps/plan/entitlements routes and supporting tenants/session changes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.3 KiB
TypeScript
50 lines
1.3 KiB
TypeScript
// Tiny JWT helpers — we never *verify* tokens client-side (the server
|
|
// is the only authority), we just decode the payload to read claims
|
|
// the UI uses for nav gating + tenant context.
|
|
|
|
export type ArcadiaClaims = {
|
|
sub?: string
|
|
email?: string
|
|
tenant_id?: string
|
|
tenant_slug?: string
|
|
roles?: string[]
|
|
available_tenants?: AvailableTenantClaim[]
|
|
exp?: number
|
|
iat?: number
|
|
[k: string]: unknown
|
|
}
|
|
|
|
export type AvailableTenantClaim = {
|
|
id?: string
|
|
slug?: string
|
|
name?: string
|
|
roles?: string[]
|
|
}
|
|
|
|
function b64urlDecode(s: string): string {
|
|
const pad = "=".repeat((4 - (s.length % 4)) % 4)
|
|
const b64 = (s + pad).replace(/-/g, "+").replace(/_/g, "/")
|
|
if (typeof atob === "function") return atob(b64)
|
|
// Node fallback (SSR / tests)
|
|
return Buffer.from(b64, "base64").toString("binary")
|
|
}
|
|
|
|
export function decodeJwt(token: string): ArcadiaClaims | null {
|
|
if (!token) return null
|
|
const parts = token.split(".")
|
|
if (parts.length !== 3) return null
|
|
try {
|
|
const raw = b64urlDecode(parts[1])
|
|
// Handle UTF-8: atob returns binary string; reconstruct UTF-8.
|
|
const utf8 =
|
|
typeof TextDecoder !== "undefined"
|
|
? new TextDecoder().decode(
|
|
Uint8Array.from(raw, (c) => c.charCodeAt(0)),
|
|
)
|
|
: raw
|
|
return JSON.parse(utf8) as ArcadiaClaims
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|