Wire operator Integrations page + capability-gating framework
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>
This commit is contained in:
49
app/lib/jwt.ts
Normal file
49
app/lib/jwt.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
// 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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user