@userbox/react 0.0.0-stage → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/LICENSE +202 -0
- package/README.md +149 -2
- package/SECURITY.md +62 -0
- package/dist/index.d.ts +161 -0
- package/dist/index.js +1199 -0
- package/dist/index.js.map +1 -0
- package/dist/nextjs.d.ts +85 -0
- package/dist/nextjs.js +257 -0
- package/dist/nextjs.js.map +1 -0
- package/package.json +86 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/core/permission-cache.ts","../src/core/redirect.ts","../src/core/http.ts","../src/core/token.ts","../src/core/refresh-engine.ts","../src/core/session-store.ts","../src/react/context.ts","../src/react/UserboxProvider.tsx","../src/react/useSession.ts","../src/react/useAccessToken.ts","../src/react/usePermissions.ts","../src/react/usePermission.ts","../src/core/state-nonce.ts","../src/react/useSignIn.ts","../src/react/useSignUp.ts","../src/react/useSignOut.ts","../src/react/UserboxCallback.tsx"],"names":["useContext","useEffect","useMemo","useCallback","useRef"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Userbox error tagged-union. Used by every layer of the SDK as the single\n * error shape callers may inspect. Tokens (access / refresh) MUST NEVER appear\n * in any `message` produced by these constructors — security invariant S-2.\n */\n\nexport type MisconfiguredField =\n | \"tenantSlug\"\n | \"baseUrl\"\n | \"tokenStorage\"\n | \"refreshUrl\"\n | \"callbackUrl\";\n\nexport type UserboxError =\n | {\n kind: \"missing_credentials\";\n message: string;\n requestId?: string;\n }\n | {\n kind: \"auth_failed\";\n message: string;\n httpStatus: number;\n code: string;\n requestId?: string;\n }\n | {\n kind: \"refresh_failed\";\n message: string;\n httpStatus: number;\n code: string;\n requestId?: string;\n }\n | {\n kind: \"permission_fetch_failed\";\n message: string;\n httpStatus: number;\n code: string;\n workspaceId: string;\n requestId?: string;\n }\n | { kind: \"network\"; message: string }\n | { kind: \"misconfigured\"; message: string; field: MisconfiguredField };\n\nconst ERROR_KINDS: ReadonlySet<string> = new Set([\n \"missing_credentials\",\n \"auth_failed\",\n \"refresh_failed\",\n \"permission_fetch_failed\",\n \"network\",\n \"misconfigured\",\n]);\n\n/** Type guard verifying a value is a `UserboxError`. */\nexport function isUserboxError(value: unknown): value is UserboxError {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as { kind?: unknown; message?: unknown };\n return (\n typeof candidate.kind === \"string\" &&\n ERROR_KINDS.has(candidate.kind) &&\n typeof candidate.message === \"string\"\n );\n}\n\nexport function missingCredentials(message: string, requestId?: string): UserboxError {\n return requestId\n ? { kind: \"missing_credentials\", message, requestId }\n : { kind: \"missing_credentials\", message };\n}\n\nexport function authFailed(\n message: string,\n httpStatus: number,\n code: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? { kind: \"auth_failed\", message, httpStatus, code, requestId }\n : { kind: \"auth_failed\", message, httpStatus, code };\n}\n\nexport function refreshFailed(\n message: string,\n httpStatus: number,\n code: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? { kind: \"refresh_failed\", message, httpStatus, code, requestId }\n : { kind: \"refresh_failed\", message, httpStatus, code };\n}\n\nexport function permissionFetchFailed(\n message: string,\n httpStatus: number,\n code: string,\n workspaceId: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? {\n kind: \"permission_fetch_failed\",\n message,\n httpStatus,\n code,\n workspaceId,\n requestId,\n }\n : { kind: \"permission_fetch_failed\", message, httpStatus, code, workspaceId };\n}\n\nexport function networkError(message: string): UserboxError {\n return { kind: \"network\", message };\n}\n\nexport function misconfigured(field: MisconfiguredField, message: string): UserboxError {\n return { kind: \"misconfigured\", message, field };\n}\n","import type { UserboxError } from \"./errors\";\n\nexport type PermissionCacheEntry =\n | { state: \"loading\"; promise: Promise<ReadonlySet<string>>; startedAt: number }\n | { state: \"loaded\"; permissions: ReadonlySet<string>; fetchedAt: number }\n | { state: \"error\"; error: UserboxError; failedAt: number; lastGood?: ReadonlySet<string> };\n\nexport class PermissionCache {\n private readonly entries = new Map<string, PermissionCacheEntry>();\n\n get(workspaceId: string): PermissionCacheEntry | undefined {\n return this.entries.get(workspaceId);\n }\n\n has(workspaceId: string): boolean {\n return this.entries.has(workspaceId);\n }\n\n setLoading(workspaceId: string, promise: Promise<ReadonlySet<string>>): void {\n this.entries.set(workspaceId, {\n state: \"loading\",\n promise,\n startedAt: Date.now(),\n });\n }\n\n setLoaded(workspaceId: string, permissions: Iterable<string>): void {\n const frozen = freezeSet(permissions);\n this.entries.set(workspaceId, {\n state: \"loaded\",\n permissions: frozen,\n fetchedAt: Date.now(),\n });\n }\n\n setError(workspaceId: string, error: UserboxError): void {\n const prior = this.entries.get(workspaceId);\n const lastGood =\n prior && prior.state === \"loaded\" ? prior.permissions : undefined;\n this.entries.set(workspaceId, lastGood !== undefined\n ? { state: \"error\", error, failedAt: Date.now(), lastGood }\n : { state: \"error\", error, failedAt: Date.now() });\n }\n\n clear(): void {\n this.entries.clear();\n }\n\n snapshot(): ReadonlyMap<string, PermissionCacheEntry> {\n return new Map(this.entries);\n }\n}\n\nfunction freezeSet(values: Iterable<string>): ReadonlySet<string> {\n const s = new Set<string>();\n for (const v of values) {\n if (typeof v === \"string\") s.add(v);\n }\n return s as ReadonlySet<string>;\n}\n","import { misconfigured, type UserboxError } from \"./errors\";\n\nconst SLUG_REGEX = /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/;\nconst MAX_RETURN_TO_LENGTH = 2048;\n\nexport function isValidSlug(slug: string): boolean {\n return SLUG_REGEX.test(slug);\n}\n\nexport function assertValidSlug(slug: string): void {\n if (typeof slug !== \"string\" || slug.length === 0) {\n throw misconfiguredField(\"tenantSlug\", \"tenantSlug is required\");\n }\n if (!SLUG_REGEX.test(slug)) {\n throw misconfiguredField(\n \"tenantSlug\",\n \"tenantSlug must match /^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/\",\n );\n }\n}\n\nexport type AuthSurfaceUrlInput = {\n tenantSlug: string;\n /**\n * Userbox API base URL — e.g. `https://api.example.com` or\n * `http://api.example.localhost`. The auth-surface origin is derived\n * from this by replacing the leading DNS label with `{slug}.auth`:\n * `https://api.example.com` → `https://{slug}.auth.example.com`\n * `http://api.example.localhost` → `http://{slug}.auth.example.localhost`\n * This convention is intentional and hardcoded — tenant code never needs\n * to configure the auth URL separately.\n */\n baseUrl: string;\n /**\n * Optional override for the auth-surface origin. Use ONLY for\n * self-hosted Userbox deployments whose hostnames don't follow the\n * standard `api.{userbox-domain}` ↔ `{slug}.auth.{userbox-domain}`\n * convention. Managed (SaaS) deployments should leave this unset.\n */\n baseAuthUrl?: string;\n path: \"/login\" | \"/signup\";\n returnTo: string;\n state: string;\n flowId: string;\n};\n\n/**\n * Derive the auth surface origin from a Userbox API base URL by replacing\n * the leading DNS label with `{slug}.auth`.\n *\n * Examples:\n * api.example.com + acme → acme.auth.example.com\n * api.example.localhost + acme → acme.auth.example.localhost\n * api.example.com:8443 + acme → acme.auth.example.com:8443\n */\nexport function deriveAuthOrigin(baseUrl: string, tenantSlug: string): string {\n let url: URL;\n try {\n url = new URL(baseUrl);\n } catch {\n throw misconfiguredField(\"baseUrl\", \"baseUrl must be a valid absolute URL\");\n }\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw misconfiguredField(\"baseUrl\", \"baseUrl must be http(s)\");\n }\n const host = url.hostname;\n const dotIdx = host.indexOf(\".\");\n if (dotIdx === -1) {\n throw misconfiguredField(\n \"baseUrl\",\n \"baseUrl host must have at least two DNS labels (e.g. api.example.localhost)\",\n );\n }\n const parent = host.substring(dotIdx + 1);\n const port = url.port ? `:${url.port}` : \"\";\n return `${url.protocol}//${tenantSlug}.auth.${parent}${port}`;\n}\n\nexport function buildAuthSurfaceUrl(input: AuthSurfaceUrlInput): string {\n assertValidSlug(input.tenantSlug);\n assertValidReturnTo(input.returnTo);\n const origin = input.baseAuthUrl ?? deriveAuthOrigin(input.baseUrl, input.tenantSlug);\n const trimmed = origin.replace(/\\/+$/, \"\");\n const u = new URL(trimmed + input.path);\n u.searchParams.set(\"return_to\", input.returnTo);\n u.searchParams.set(\"state\", input.state);\n u.searchParams.set(\"flow_id\", input.flowId);\n return u.toString();\n}\n\nexport function assertValidReturnTo(returnTo: string, currentOrigin?: string): void {\n if (typeof returnTo !== \"string\" || returnTo.length === 0) {\n throw misconfiguredField(\"callbackUrl\", \"returnTo is required\");\n }\n if (returnTo.length > MAX_RETURN_TO_LENGTH) {\n throw misconfiguredField(\n \"callbackUrl\",\n `returnTo exceeds maximum length (${MAX_RETURN_TO_LENGTH})`,\n );\n }\n let parsed: URL;\n try {\n parsed = new URL(returnTo);\n } catch {\n throw misconfiguredField(\"callbackUrl\", \"returnTo must be an absolute URL\");\n }\n if (parsed.protocol !== \"https:\" && parsed.protocol !== \"http:\") {\n throw misconfiguredField(\"callbackUrl\", \"returnTo must be http(s)\");\n }\n const origin = currentOrigin ?? safeWindowOrigin();\n if (origin && parsed.origin !== origin) {\n throw misconfiguredField(\n \"callbackUrl\",\n \"returnTo must be same-origin with the calling app\",\n );\n }\n}\n\nfunction safeWindowOrigin(): string | null {\n if (typeof window === \"undefined\") return null;\n if (typeof window.location?.origin !== \"string\") return null;\n return window.location.origin;\n}\n\nfunction misconfiguredField(\n field: Parameters<typeof misconfigured>[0],\n message: string,\n): UserboxError {\n return misconfigured(field, message);\n}\n","import {\n authFailed,\n networkError,\n refreshFailed,\n permissionFetchFailed,\n type UserboxError,\n} from \"./errors\";\n\nexport type HttpRequestInput = {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\" | \"PUT\" | \"PATCH\";\n tenantSlug: string;\n accessToken?: string | null;\n body?: unknown;\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n signal?: AbortSignal;\n /**\n * Maps a non-2xx response to the right `UserboxError` kind. Each caller\n * names its own context (refresh / permissions / etc.) so the error surface\n * stays meaningful.\n */\n errorKind: \"auth\" | \"refresh\" | \"permissions\";\n workspaceId?: string;\n};\n\nexport type HttpResponse<T> =\n | { ok: true; data: T; requestId?: string }\n | { ok: false; error: UserboxError };\n\ntype ServerErrorShape = {\n code?: unknown;\n error?: unknown;\n};\n\nexport async function request<T>(input: HttpRequestInput): Promise<HttpResponse<T>> {\n const headers: Record<string, string> = {\n \"X-Tenant-ID\": input.tenantSlug,\n Accept: \"application/json\",\n ...input.headers,\n };\n if (input.accessToken) {\n headers.Authorization = `Bearer ${input.accessToken}`;\n }\n const init: RequestInit = {\n method: input.method ?? \"GET\",\n headers,\n };\n if (input.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(input.body);\n }\n if (input.credentials !== undefined) {\n init.credentials = input.credentials;\n }\n if (input.signal) init.signal = input.signal;\n\n let response: Response;\n try {\n response = await fetch(input.url, init);\n } catch (err) {\n return {\n ok: false,\n error: networkError(humaniseNetworkError(err)),\n };\n }\n\n const requestId = response.headers.get(\"X-Request-ID\") ?? undefined;\n\n if (response.status === 204) {\n return { ok: true, data: undefined as T, requestId };\n }\n\n let parsed: unknown = null;\n const text = await response.text();\n if (text.length > 0) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n }\n\n if (response.ok) {\n return { ok: true, data: parsed as T, requestId };\n }\n\n const shape = (parsed ?? {}) as ServerErrorShape;\n const code = typeof shape.code === \"string\" ? shape.code : \"UNKNOWN\";\n const message =\n typeof shape.error === \"string\"\n ? shape.error\n : `HTTP ${response.status}`;\n const error = mapServerError(input, response.status, code, message, requestId);\n return { ok: false, error };\n}\n\nfunction mapServerError(\n input: HttpRequestInput,\n status: number,\n code: string,\n message: string,\n requestId?: string,\n): UserboxError {\n switch (input.errorKind) {\n case \"refresh\":\n return refreshFailed(message, status, code, requestId);\n case \"permissions\":\n return permissionFetchFailed(\n message,\n status,\n code,\n input.workspaceId ?? \"\",\n requestId,\n );\n case \"auth\":\n default:\n return authFailed(message, status, code, requestId);\n }\n}\n\nfunction humaniseNetworkError(err: unknown): string {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return \"Request aborted\";\n }\n if (err instanceof Error) return err.message || \"Network error\";\n return \"Network error\";\n}\n","import { authFailed, type UserboxError } from \"./errors\";\n\nexport type JwtClaims = {\n sub: string;\n tid: string;\n exp: number;\n jti?: string;\n};\n\nfunction base64UrlDecode(input: string): string {\n let s = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (s.length % 4 !== 0) s += \"=\";\n if (typeof atob === \"function\") return atob(s);\n return Buffer.from(s, \"base64\").toString(\"binary\");\n}\n\n/**\n * Decode JWT claims without signature verification. The server is the only\n * authority on token validity; the SDK uses\n * `exp` only to drive its proactive-refresh timer.\n */\nexport function decodeJwtClaims(token: string): JwtClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw asAuthFailed(\"Malformed JWT\");\n }\n const payload = parts[1];\n if (!payload) throw asAuthFailed(\"Malformed JWT payload\");\n\n let json: string;\n try {\n json = base64UrlDecode(payload);\n } catch {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n\n let obj: unknown;\n try {\n obj = JSON.parse(json);\n } catch {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n\n if (typeof obj !== \"object\" || obj === null) {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n const claims = obj as Record<string, unknown>;\n const sub = typeof claims.sub === \"string\" ? claims.sub : null;\n const tid = typeof claims.tid === \"string\" ? claims.tid : null;\n const exp = typeof claims.exp === \"number\" ? claims.exp : null;\n const jti = typeof claims.jti === \"string\" ? claims.jti : undefined;\n if (!sub || !tid || exp === null) {\n throw asAuthFailed(\"JWT missing required claims (sub/tid/exp)\");\n }\n return jti ? { sub, tid, exp, jti } : { sub, tid, exp };\n}\n\nfunction asAuthFailed(message: string): UserboxError {\n return authFailed(message, 401, \"MALFORMED_JWT\");\n}\n","import { authFailed, type UserboxError } from \"./errors\";\nimport { request } from \"./http\";\nimport { decodeJwtClaims, type JwtClaims } from \"./token\";\nimport type { SessionStore, TokenStorage } from \"./session-store\";\n\nexport type SessionUser = {\n id: string;\n tenantId: string;\n email: string | null;\n fullName: string | null;\n};\n\nexport type Session = {\n accessToken: string;\n accessTokenExpiresAt: number;\n refreshExpiresAt: number;\n user: SessionUser;\n};\n\nexport type RefreshSuccess = {\n kind: \"ok\";\n session: Session;\n rawRefreshToken: string | null;\n nextRefreshIn: number;\n};\n\nexport type RefreshPermanent = {\n kind: \"permanent_fail\";\n error: UserboxError;\n};\n\nexport type RefreshTransient = {\n kind: \"transient_fail\";\n error: UserboxError;\n retryInMs: number;\n};\n\nexport type RefreshResult = RefreshSuccess | RefreshPermanent | RefreshTransient;\n\nexport type RefreshEngineOptions = {\n tenantSlug: string;\n baseUrl: string;\n refreshUrl: string;\n tokenStorage: TokenStorage;\n sessionStore: SessionStore;\n refreshThresholdPct: number; // 0..1 — refresh when this fraction of TTL is REMAINING\n proactiveSkewMs: number;\n onSession: (session: Session) => void;\n onSessionExpired: () => void;\n onError: (err: UserboxError) => void;\n /**\n * BroadcastChannel name suffix (typically tenant slug). When provided and\n * `BroadcastChannel` is available, refresh requests are coordinated across\n * tabs so that only one tab issues the outbound HTTP call.\n */\n broadcastChannelName?: string;\n /** Test hooks. */\n now?: () => number;\n};\n\ntype RefreshHttpResponse = {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n refresh_expires_in?: unknown;\n};\n\nconst RETRY_DELAYS_MS = [1_000, 3_000, 9_000];\nconst PERMANENT_HTTP_STATUSES: ReadonlySet<number> = new Set([400, 401, 403]);\nconst CLAIM_WINDOW_MS = 200;\n\nexport class RefreshEngine {\n private readonly options: RefreshEngineOptions;\n private readonly now: () => number;\n private timer: ReturnType<typeof setTimeout> | null = null;\n private inFlight: Promise<RefreshResult> | null = null;\n private failureCount = 0;\n private permanentlyFailed = false;\n private sessionExpiredFired = false;\n private channel: BroadcastChannel | null = null;\n private currentSession: Session | null = null;\n\n constructor(options: RefreshEngineOptions) {\n this.options = options;\n this.now = options.now ?? Date.now;\n this.setupChannel();\n }\n\n private setupChannel(): void {\n if (!this.options.broadcastChannelName) return;\n if (typeof BroadcastChannel === \"undefined\") return;\n try {\n this.channel = new BroadcastChannel(\n `userbox-refresh-${this.options.broadcastChannelName}`,\n );\n this.channel.addEventListener(\"message\", (evt: MessageEvent) => {\n this.handleBroadcast(evt.data);\n });\n } catch {\n this.channel = null;\n }\n }\n\n private handleBroadcast(data: unknown): void {\n if (!data || typeof data !== \"object\") return;\n const msg = data as { type?: unknown; session?: unknown };\n if (msg.type !== \"session\" || typeof msg.session !== \"object\" || msg.session === null) return;\n const session = msg.session as Partial<Session>;\n if (\n typeof session.accessToken !== \"string\" ||\n typeof session.accessTokenExpiresAt !== \"number\" ||\n typeof session.refreshExpiresAt !== \"number\" ||\n typeof session.user !== \"object\" ||\n session.user === null\n ) {\n return;\n }\n const u = session.user as Partial<SessionUser>;\n if (typeof u.id !== \"string\" || typeof u.tenantId !== \"string\") return;\n const normalised: Session = {\n accessToken: session.accessToken,\n accessTokenExpiresAt: session.accessTokenExpiresAt,\n refreshExpiresAt: session.refreshExpiresAt,\n user: {\n id: u.id,\n tenantId: u.tenantId,\n email: typeof u.email === \"string\" ? u.email : null,\n fullName: typeof u.fullName === \"string\" ? u.fullName : null,\n },\n };\n this.adoptSession(normalised);\n }\n\n private adoptSession(session: Session): void {\n if (this.currentSession && this.currentSession.user.id !== session.user.id) {\n // Identity swap is a hard error per data-model I-2 — surface and stop.\n this.permanentlyFailed = true;\n this.clearTimer();\n this.options.onError(authFailed(\"Session identity changed unexpectedly\", 401, \"IDENTITY_SWAP\"));\n this.fireSessionExpiredOnce();\n return;\n }\n this.currentSession = session;\n this.options.onSession(session);\n this.scheduleNextRefresh();\n }\n\n /**\n * Adopt the initial session (e.g. from the callback flow). Subsequent\n * refreshes are scheduled automatically.\n */\n primeSession(session: Session): void {\n this.permanentlyFailed = false;\n this.sessionExpiredFired = false;\n this.failureCount = 0;\n this.currentSession = session;\n this.scheduleNextRefresh();\n }\n\n hasSession(): boolean {\n return this.currentSession !== null;\n }\n\n isPermanentlyFailed(): boolean {\n return this.permanentlyFailed;\n }\n\n getSession(): Session | null {\n return this.currentSession;\n }\n\n async refresh(): Promise<RefreshResult> {\n if (this.permanentlyFailed) {\n return {\n kind: \"permanent_fail\",\n error: authFailed(\"Refresh already permanently failed\", 401, \"PERMANENT_FAIL\"),\n };\n }\n if (this.inFlight) return this.inFlight;\n this.inFlight = this.performRefreshWithCoordination().finally(() => {\n this.inFlight = null;\n });\n return this.inFlight;\n }\n\n private async performRefreshWithCoordination(): Promise<RefreshResult> {\n if (this.channel) {\n // Best-effort cross-tab claim: announce intent and wait briefly for a\n // session broadcast from another tab. If one arrives first, adopt it\n // and skip the HTTP call.\n const winner = await this.claimRefreshWindow();\n if (winner) return winner;\n }\n return this.performRefresh();\n }\n\n private claimRefreshWindow(): Promise<RefreshResult | null> {\n return new Promise<RefreshResult | null>((resolve) => {\n let resolved = false;\n const channel = this.channel;\n if (!channel) {\n resolve(null);\n return;\n }\n const listener = (evt: MessageEvent): void => {\n if (resolved) return;\n if (!evt.data || typeof evt.data !== \"object\") return;\n const msg = evt.data as { type?: unknown; session?: unknown };\n if (msg.type !== \"session\") return;\n const previous = this.currentSession;\n this.handleBroadcast(evt.data);\n // The broadcast adopter does not return a RefreshResult directly; we\n // synthesise one from `currentSession` if it changed.\n if (this.currentSession && this.currentSession !== previous) {\n resolved = true;\n channel.removeEventListener(\"message\", listener);\n resolve({\n kind: \"ok\",\n session: this.currentSession,\n rawRefreshToken: null,\n nextRefreshIn: this.computeNextRefreshIn(this.currentSession),\n });\n }\n };\n channel.addEventListener(\"message\", listener);\n try {\n channel.postMessage({ type: \"claim\" });\n } catch {\n // ignore\n }\n setTimeout(() => {\n if (resolved) return;\n channel.removeEventListener(\"message\", listener);\n resolve(null);\n }, CLAIM_WINDOW_MS);\n });\n }\n\n private async performRefresh(): Promise<RefreshResult> {\n const body =\n this.options.tokenStorage === \"memory\"\n ? { refresh_token: this.options.sessionStore.getRefreshToken() ?? \"\" }\n : {};\n\n const res = await request<RefreshHttpResponse>({\n url: this.options.refreshUrl,\n method: \"POST\",\n tenantSlug: this.options.tenantSlug,\n body,\n // Under cookie mode the refresh cookie may live on a different origin\n // (tenant backend). Same-origin keeps memory mode safe.\n credentials: this.options.tokenStorage === \"cookie\" ? \"include\" : \"same-origin\",\n errorKind: \"refresh\",\n });\n\n if (!res.ok) {\n const error = res.error;\n const status =\n error.kind === \"refresh_failed\" || error.kind === \"auth_failed\"\n ? error.httpStatus\n : 0;\n if (\n error.kind === \"refresh_failed\" &&\n PERMANENT_HTTP_STATUSES.has(status)\n ) {\n return this.transitionPermanent(error);\n }\n return this.transitionTransient(error);\n }\n\n const data = res.data;\n const accessToken =\n typeof data.access_token === \"string\" ? data.access_token : \"\";\n const expiresIn =\n typeof data.expires_in === \"number\" ? data.expires_in : 0;\n const refreshExpiresIn =\n typeof data.refresh_expires_in === \"number\"\n ? data.refresh_expires_in\n : expiresIn;\n if (!accessToken || expiresIn <= 0) {\n return this.transitionTransient(\n authFailed(\"Refresh response missing access_token / expires_in\", 500, \"MALFORMED_RESPONSE\"),\n );\n }\n\n let claims: JwtClaims;\n try {\n claims = decodeJwtClaims(accessToken);\n } catch (e) {\n const err = e as UserboxError;\n return this.transitionPermanent(err);\n }\n\n const newRefreshToken =\n typeof data.refresh_token === \"string\" ? data.refresh_token : null;\n if (this.options.tokenStorage === \"memory\" && newRefreshToken) {\n this.options.sessionStore.setRefreshToken(newRefreshToken);\n }\n\n const previousUserId = this.currentSession?.user.id ?? null;\n if (previousUserId && previousUserId !== claims.sub) {\n const err = authFailed(\"Session identity changed unexpectedly\", 401, \"IDENTITY_SWAP\");\n return this.transitionPermanent(err);\n }\n\n const session: Session = {\n accessToken,\n accessTokenExpiresAt: this.now() + expiresIn * 1000,\n refreshExpiresAt: this.now() + refreshExpiresIn * 1000,\n user: {\n id: claims.sub,\n tenantId: claims.tid,\n email: this.currentSession?.user.email ?? null,\n fullName: this.currentSession?.user.fullName ?? null,\n },\n };\n\n this.failureCount = 0;\n this.currentSession = session;\n this.options.onSession(session);\n this.scheduleNextRefresh();\n this.broadcast(session);\n\n return {\n kind: \"ok\",\n session,\n rawRefreshToken: newRefreshToken,\n nextRefreshIn: this.computeNextRefreshIn(session),\n };\n }\n\n private transitionPermanent(error: UserboxError): RefreshPermanent {\n this.permanentlyFailed = true;\n this.clearTimer();\n this.options.onError(error);\n this.fireSessionExpiredOnce();\n return { kind: \"permanent_fail\", error };\n }\n\n private transitionTransient(error: UserboxError): RefreshTransient {\n this.failureCount += 1;\n this.options.onError(error);\n if (this.failureCount >= RETRY_DELAYS_MS.length) {\n // Escalate to permanent for this lifetime.\n const escalated = authFailed(\n \"Refresh transient failures exceeded retry budget\",\n 503,\n \"REFRESH_RETRY_BUDGET\",\n );\n this.transitionPermanent(escalated);\n return { kind: \"transient_fail\", error, retryInMs: 0 };\n }\n const retryInMs = RETRY_DELAYS_MS[this.failureCount - 1] ?? 9_000;\n this.scheduleTimer(retryInMs);\n return { kind: \"transient_fail\", error, retryInMs };\n }\n\n private fireSessionExpiredOnce(): void {\n if (this.sessionExpiredFired) return;\n this.sessionExpiredFired = true;\n this.options.onSessionExpired();\n }\n\n private computeNextRefreshIn(session: Session): number {\n const ttl = session.accessTokenExpiresAt - this.now();\n const reserved = Math.max(\n ttl * this.options.refreshThresholdPct,\n this.options.proactiveSkewMs,\n );\n const next = ttl - reserved;\n return Math.max(next, 0);\n }\n\n private scheduleNextRefresh(): void {\n if (!this.currentSession) return;\n if (this.permanentlyFailed) return;\n const delay = this.computeNextRefreshIn(this.currentSession);\n this.scheduleTimer(delay);\n }\n\n private scheduleTimer(delay: number): void {\n this.clearTimer();\n this.timer = setTimeout(() => {\n this.timer = null;\n void this.refresh();\n }, delay);\n }\n\n private clearTimer(): void {\n if (this.timer !== null) {\n clearTimeout(this.timer);\n this.timer = null;\n }\n }\n\n private broadcast(session: Session): void {\n if (!this.channel) return;\n try {\n this.channel.postMessage({ type: \"session\", session });\n } catch {\n // ignore\n }\n }\n\n /** Test/teardown hook. */\n dispose(): void {\n this.clearTimer();\n if (this.channel) {\n try {\n this.channel.close();\n } catch {\n // ignore\n }\n this.channel = null;\n }\n }\n}\n","/**\n * Storage strategy abstraction for the refresh credential.\n *\n * - `MemoryStore` holds the refresh token in a closure variable (V1 SPA mode).\n * - `CookieStore` is a stub: it never touches `document.cookie`. The refresh\n * cookie is set/cleared by the tenant's backend in response to the\n * `refreshUrl` POST or `Set-Cookie` header from the Edge middleware. Its\n * read/write methods are no-ops by design.\n */\n\nexport type TokenStorage = \"memory\" | \"cookie\";\n\nexport interface SessionStore {\n readonly kind: TokenStorage;\n getRefreshToken(): string | null;\n setRefreshToken(token: string | null): void;\n}\n\nexport class MemoryStore implements SessionStore {\n readonly kind: TokenStorage = \"memory\";\n private token: string | null = null;\n getRefreshToken(): string | null {\n return this.token;\n }\n setRefreshToken(token: string | null): void {\n this.token = token;\n }\n}\n\nexport class CookieStore implements SessionStore {\n readonly kind: TokenStorage = \"cookie\";\n // The browser owns the cookie; the SDK never reads or writes it directly.\n getRefreshToken(): string | null {\n return null;\n }\n setRefreshToken(_token: string | null): void {\n void _token;\n }\n}\n\nexport function createSessionStore(kind: TokenStorage): SessionStore {\n return kind === \"memory\" ? new MemoryStore() : new CookieStore();\n}\n","import { createContext } from \"react\";\nimport type { PermissionCache, PermissionCacheEntry } from \"../core/permission-cache\";\nimport type { Session, SessionUser } from \"../core/refresh-engine\";\nimport type { UserboxError } from \"../core/errors\";\n\nexport type PublicSession = {\n user: SessionUser;\n};\n\nexport type SessionResult =\n | { isLoaded: false; isSignedIn: false; user: null }\n | { isLoaded: true; isSignedIn: false; user: null }\n | { isLoaded: true; isSignedIn: true; user: SessionUser };\n\nexport type PermissionsResult = {\n permissions: ReadonlySet<string>;\n isLoaded: boolean;\n error: UserboxError | null;\n};\n\nexport type PermissionResult = {\n allowed: boolean;\n isLoaded: boolean;\n};\n\nexport type ProviderContextValue = {\n /** Snapshot of the session for `useSession`. */\n session: Session | null;\n isLoaded: boolean;\n /** Provider-internal permission cache. */\n permissionCache: PermissionCache;\n /** Fetch permissions for a workspace; safe to call from a hook effect. */\n ensurePermissions: (workspaceId: string) => void;\n /** Force the provider to re-read the permission cache (re-render subscribers). */\n permissionsVersion: number;\n /** Sign-in/sign-up/sign-out plumbing. */\n navigate: (url: string) => void;\n signOut: (redirectUri?: string) => Promise<void>;\n /** Tenant config (frozen). */\n config: ProviderConfigInternal;\n /** Refresh engine handle for internal hooks. */\n refreshEngine: import(\"../core/refresh-engine\").RefreshEngine;\n /** Adopt a session from the callback flow. */\n adoptCallbackSession: (session: Session, rawRefreshToken: string | null) => void;\n};\n\nexport type ProviderConfigInternal = Readonly<{\n tenantSlug: string;\n baseUrl: string;\n baseAuthUrl?: string;\n tokenStorage: \"memory\" | \"cookie\";\n refreshUrl: string;\n refreshThresholdPct: number;\n proactiveSkewMs: number;\n onSessionExpired?: () => void;\n onError?: (err: UserboxError) => void;\n}>;\n\nexport const UserboxContext = createContext<ProviderContextValue | null>(null);\n\nexport type { Session, SessionUser, PermissionCacheEntry };\n","import {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport * as React from \"react\";\nimport { authFailed, misconfigured, type UserboxError } from \"../core/errors\";\nimport { PermissionCache } from \"../core/permission-cache\";\nimport { assertValidSlug } from \"../core/redirect\";\nimport { request } from \"../core/http\";\nimport {\n RefreshEngine,\n type Session,\n type SessionUser,\n} from \"../core/refresh-engine\";\nimport { createSessionStore, type SessionStore } from \"../core/session-store\";\nimport {\n UserboxContext,\n type ProviderConfigInternal,\n type ProviderContextValue,\n} from \"./context\";\n\nexport type UserboxProviderProps = {\n tenantSlug: string;\n baseUrl: string;\n baseAuthUrl?: string;\n tokenStorage: \"cookie\" | \"memory\";\n refreshUrl?: string;\n refreshThresholdPct?: number;\n proactiveSkewMs?: number;\n onSessionExpired?: () => void;\n onError?: (err: UserboxError) => void;\n children: React.ReactNode;\n};\n\nconst DEFAULT_REFRESH_THRESHOLD_PCT = 0.2;\nconst DEFAULT_PROACTIVE_SKEW_MS = 5_000;\n\nfunction validateConfig(props: UserboxProviderProps): ProviderConfigInternal {\n if (typeof props.tenantSlug !== \"string\" || props.tenantSlug.length === 0) {\n throw misconfigured(\"tenantSlug\", \"tenantSlug is required\");\n }\n assertValidSlug(props.tenantSlug);\n if (typeof props.baseUrl !== \"string\" || props.baseUrl.length === 0) {\n throw misconfigured(\"baseUrl\", \"baseUrl is required\");\n }\n if (props.baseUrl.endsWith(\"/\")) {\n throw misconfigured(\"baseUrl\", \"baseUrl must not have a trailing slash\");\n }\n if (props.tokenStorage !== \"memory\" && props.tokenStorage !== \"cookie\") {\n throw misconfigured(\"tokenStorage\", \"tokenStorage must be 'memory' or 'cookie'\");\n }\n const refreshUrl =\n props.refreshUrl ??\n (props.tokenStorage === \"cookie\"\n ? \"/api/auth/refresh\"\n : `${props.baseUrl}/v1/auth/refresh`);\n const config: ProviderConfigInternal = Object.freeze({\n tenantSlug: props.tenantSlug,\n baseUrl: props.baseUrl,\n baseAuthUrl: props.baseAuthUrl,\n tokenStorage: props.tokenStorage,\n refreshUrl,\n refreshThresholdPct: props.refreshThresholdPct ?? DEFAULT_REFRESH_THRESHOLD_PCT,\n proactiveSkewMs: props.proactiveSkewMs ?? DEFAULT_PROACTIVE_SKEW_MS,\n onSessionExpired: props.onSessionExpired,\n onError: props.onError,\n });\n return config;\n}\n\ntype ProviderInternalState = {\n session: Session | null;\n isLoaded: boolean;\n permissionsVersion: number;\n};\n\nexport const UserboxProvider: React.FC<UserboxProviderProps> = (props) => {\n // Forbid nested mounts.\n const parent = React.useContext(UserboxContext);\n if (parent) {\n throw misconfigured(\"tenantSlug\", \"UserboxProvider cannot be nested\");\n }\n\n const config = useMemo(() => validateConfig(props), [\n props.tenantSlug,\n props.baseUrl,\n props.baseAuthUrl,\n props.tokenStorage,\n props.refreshUrl,\n props.refreshThresholdPct,\n props.proactiveSkewMs,\n ]);\n\n const [state, setState] = useState<ProviderInternalState>({\n session: null,\n isLoaded: false,\n permissionsVersion: 0,\n });\n const cacheRef = useRef<PermissionCache>(new PermissionCache());\n const sessionStoreRef = useRef<SessionStore | null>(null);\n const engineRef = useRef<RefreshEngine | null>(null);\n const userMeFetchedRef = useRef<string | null>(null);\n\n if (!sessionStoreRef.current) {\n sessionStoreRef.current = createSessionStore(config.tokenStorage);\n }\n\n // Build the refresh engine exactly once per provider mount.\n if (!engineRef.current) {\n engineRef.current = new RefreshEngine({\n tenantSlug: config.tenantSlug,\n baseUrl: config.baseUrl,\n refreshUrl: config.refreshUrl,\n tokenStorage: config.tokenStorage,\n sessionStore: sessionStoreRef.current,\n refreshThresholdPct: config.refreshThresholdPct,\n proactiveSkewMs: config.proactiveSkewMs,\n onSession: (session) => {\n setState((s) => ({ ...s, session, isLoaded: true }));\n },\n onSessionExpired: () => {\n cacheRef.current.clear();\n setState((s) => ({\n session: null,\n isLoaded: true,\n permissionsVersion: s.permissionsVersion + 1,\n }));\n config.onSessionExpired?.();\n },\n onError: (err) => {\n config.onError?.(err);\n },\n broadcastChannelName: config.tenantSlug,\n });\n }\n\n // Bootstrap on mount.\n useEffect(() => {\n const engine = engineRef.current!;\n let cancelled = false;\n void (async () => {\n const result = await engine.refresh();\n if (cancelled) return;\n if (result.kind !== \"ok\") {\n setState((s) => ({ ...s, session: null, isLoaded: true }));\n }\n })();\n return () => {\n cancelled = true;\n engine.dispose();\n };\n\n }, []);\n\n // Lazy GET /v1/users/me to populate email + fullName.\n useEffect(() => {\n const session = state.session;\n if (!session) return;\n if (userMeFetchedRef.current === session.user.id && session.user.email !== null) {\n return;\n }\n let cancelled = false;\n void (async () => {\n const res = await request<{\n id?: unknown;\n email?: unknown;\n full_name?: unknown;\n }>({\n url: `${config.baseUrl}/v1/users/me`,\n tenantSlug: config.tenantSlug,\n accessToken: session.accessToken,\n errorKind: \"auth\",\n });\n if (cancelled) return;\n if (!res.ok) {\n // Fire-and-forget: leave email/fullName null; retry next refresh tick.\n return;\n }\n const data = res.data;\n const email = typeof data?.email === \"string\" ? data.email : null;\n const fullName = typeof data?.full_name === \"string\" ? data.full_name : null;\n userMeFetchedRef.current = session.user.id;\n setState((s) => {\n if (!s.session || s.session.user.id !== session.user.id) return s;\n const next: Session = {\n ...s.session,\n user: { ...s.session.user, email, fullName },\n };\n return { ...s, session: next };\n });\n })();\n return () => {\n cancelled = true;\n };\n }, [state.session, config.baseUrl, config.tenantSlug]);\n\n const ensurePermissions = useCallback(\n (workspaceId: string) => {\n const cache = cacheRef.current;\n if (cache.has(workspaceId)) return;\n const engine = engineRef.current!;\n const session = engine.getSession();\n if (!session) return;\n const url = `${config.baseUrl}/v1/access/permissions?user_id=${encodeURIComponent(\n session.user.id,\n )}&workspace_id=${encodeURIComponent(workspaceId)}`;\n const promise = (async () => {\n const res = await request<{ permissions?: unknown }>({\n url,\n tenantSlug: config.tenantSlug,\n accessToken: session.accessToken,\n errorKind: \"permissions\",\n workspaceId,\n });\n if (!res.ok) {\n cache.setError(workspaceId, res.error);\n config.onError?.(res.error);\n setState((s) => ({ ...s, permissionsVersion: s.permissionsVersion + 1 }));\n return new Set<string>();\n }\n const list = Array.isArray(res.data?.permissions)\n ? (res.data!.permissions as unknown[]).filter(\n (x): x is string => typeof x === \"string\",\n )\n : [];\n cache.setLoaded(workspaceId, list);\n setState((s) => ({ ...s, permissionsVersion: s.permissionsVersion + 1 }));\n return new Set(list);\n })();\n cache.setLoading(workspaceId, promise);\n },\n [config.baseUrl, config.tenantSlug],\n );\n\n const adoptCallbackSession = useCallback(\n (session: Session, rawRefreshToken: string | null) => {\n const engine = engineRef.current!;\n if (config.tokenStorage === \"memory\" && rawRefreshToken) {\n sessionStoreRef.current?.setRefreshToken(rawRefreshToken);\n }\n engine.primeSession(session);\n setState((s) => ({ ...s, session, isLoaded: true }));\n },\n [config.tokenStorage],\n );\n\n const navigate = useCallback((url: string) => {\n if (typeof window !== \"undefined\") {\n window.location.href = url;\n }\n }, []);\n\n const signOut = useCallback(\n async (redirectUri?: string) => {\n const engine = engineRef.current!;\n const session = engine.getSession();\n const refreshToken =\n config.tokenStorage === \"memory\"\n ? sessionStoreRef.current?.getRefreshToken() ?? null\n : null;\n // Step 1: clear in-memory state immediately.\n sessionStoreRef.current?.setRefreshToken(null);\n cacheRef.current.clear();\n setState((s) => ({\n session: null,\n isLoaded: true,\n permissionsVersion: s.permissionsVersion + 1,\n }));\n\n // Step 2 (memory mode): server-side revoke (best effort). In cookie\n // mode the refresh token lives in the tenant backend's httpOnly cookie,\n // so the SDK cannot send it; the backend revokes in step 3 instead.\n if (config.tokenStorage === \"memory\") try {\n const body = { refresh_token: refreshToken ?? \"\" };\n const res = await request<unknown>({\n url: `${config.baseUrl}/v1/auth/logout`,\n method: \"POST\",\n tenantSlug: config.tenantSlug,\n accessToken: session?.accessToken ?? null,\n body,\n credentials: \"same-origin\",\n errorKind: \"auth\",\n });\n if (!res.ok) {\n const err = res.error;\n if (\n !(err.kind === \"auth_failed\" && err.code === \"INVALID_REFRESH_TOKEN\")\n ) {\n config.onError?.(err);\n }\n }\n } catch (err) {\n config.onError?.(authFailed(\"logout failed\", 0, \"LOGOUT_FAILED\"));\n void err;\n }\n\n // Step 3 (cookie mode): ask the tenant backend to revoke the platform\n // session (it holds the refresh token) and clear its cookie. The access\n // token goes along because platform logout requires it.\n if (config.tokenStorage === \"cookie\") {\n try {\n await request<unknown>({\n url: config.refreshUrl,\n method: \"POST\",\n tenantSlug: config.tenantSlug,\n accessToken: session?.accessToken ?? null,\n body: { action: \"logout\" },\n credentials: \"include\",\n errorKind: \"auth\",\n });\n } catch {\n // ignore\n }\n }\n\n // Step 5: navigate.\n navigate(redirectUri ?? (typeof window !== \"undefined\" ? window.location.origin : \"/\"));\n },\n [config, navigate],\n );\n\n const contextValue: ProviderContextValue = useMemo(\n () => ({\n session: state.session,\n isLoaded: state.isLoaded,\n permissionCache: cacheRef.current,\n ensurePermissions,\n permissionsVersion: state.permissionsVersion,\n navigate,\n signOut,\n config,\n refreshEngine: engineRef.current!,\n adoptCallbackSession,\n }),\n [\n state.session,\n state.isLoaded,\n state.permissionsVersion,\n ensurePermissions,\n navigate,\n signOut,\n config,\n adoptCallbackSession,\n ],\n );\n\n // Re-export user type for downstream consumers\n void ({} as SessionUser);\n\n return (\n <UserboxContext.Provider value={contextValue}>\n {props.children}\n </UserboxContext.Provider>\n );\n};\n","import { useContext } from \"react\";\nimport { UserboxContext, type SessionResult } from \"./context\";\nimport { misconfigured } from \"../core/errors\";\n\n/**\n * Returns the session state as a discriminated union. Never throws.\n *\n * Must be called inside a `<UserboxProvider>`. Throws if used outside one\n * (misconfigured error) — that throw is a developer-time signal, not a runtime\n * error to handle in production code.\n */\nexport function useSession(): SessionResult {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"useSession() must be called inside a <UserboxProvider>\",\n );\n }\n if (!ctx.isLoaded) {\n return { isLoaded: false, isSignedIn: false, user: null };\n }\n if (!ctx.session) {\n return { isLoaded: true, isSignedIn: false, user: null };\n }\n return { isLoaded: true, isSignedIn: true, user: ctx.session.user };\n}\n","import { useContext } from \"react\";\nimport { UserboxContext } from \"./context\";\nimport { misconfigured } from \"../core/errors\";\n\n/**\n * Returns the current access token (or `null` when not signed in).\n *\n * Use this to authenticate calls from tenant frontend code to the tenant's\n * own backend (which validates the token via the Python SDK's `require_auth`\n * dependency or equivalent). The value comes from the in-memory session\n * managed by `<UserboxProvider>` — same source as the `Authorization`\n * header used internally by `useSignOut()` / refresh / `usePermissions`.\n *\n * Security note: the access token has the same exposure surface as any\n * JavaScript variable in the page. If your threat model requires the token\n * never to be reachable from user code, use a backend-cookie strategy\n * (`tokenStorage=\"cookie\"` + a refresh proxy) instead.\n */\nexport function useAccessToken(): string | null {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"useAccessToken() must be called inside a <UserboxProvider>\",\n );\n }\n return ctx.session?.accessToken ?? null;\n}\n","import { useContext, useEffect, useMemo } from \"react\";\nimport { UserboxContext, type PermissionsResult } from \"./context\";\nimport { misconfigured } from \"../core/errors\";\n\nconst EMPTY_SET: ReadonlySet<string> = new Set();\n\n/**\n * Returns the cached permission set for `workspaceId`. Issues at most one\n * outbound `GET /v1/access/permissions` per workspace (cached for the\n * lifetime of the provider).\n *\n * IMPORTANT — UI HINTS ONLY: the returned set is for show/hide rendering only.\n * The server enforces permissions on every request that performs an action.\n */\nexport function usePermissions(workspaceId: string): PermissionsResult {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"usePermissions() must be called inside a <UserboxProvider>\",\n );\n }\n\n useEffect(() => {\n if (!workspaceId) return;\n if (!ctx.session) return;\n ctx.ensurePermissions(workspaceId);\n }, [workspaceId, ctx.session, ctx]);\n\n return useMemo(() => {\n if (!workspaceId) {\n return { permissions: EMPTY_SET, isLoaded: false, error: null };\n }\n const entry = ctx.permissionCache.get(workspaceId);\n if (!entry) {\n return { permissions: EMPTY_SET, isLoaded: false, error: null };\n }\n if (entry.state === \"loaded\") {\n return { permissions: entry.permissions, isLoaded: true, error: null };\n }\n if (entry.state === \"error\") {\n return {\n permissions: entry.lastGood ?? EMPTY_SET,\n isLoaded: true,\n error: entry.error,\n };\n }\n return { permissions: EMPTY_SET, isLoaded: false, error: null };\n // Depend on permissionsVersion so consumers re-render when the cache\n // mutates (the cache itself is mutable, so React can't see it directly).\n }, [workspaceId, ctx.permissionCache, ctx.permissionsVersion]);\n}\n","import { usePermissions } from \"./usePermissions\";\nimport type { PermissionResult } from \"./context\";\n\n/**\n * Returns whether the current user holds `permissionKey` in `workspaceId`,\n * as a synchronous derivation from `usePermissions`.\n *\n * IMPORTANT — UI HINTS ONLY: this result is for show/hide rendering decisions\n * only. The server must always enforce permissions on the request that\n * performs the operation. A frontend `allowed: true` does NOT authorize a\n * destructive action — that is the responsibility of the API the action\n * targets.\n */\nexport function usePermission(\n workspaceId: string,\n permissionKey: string,\n): PermissionResult {\n const { permissions, isLoaded } = usePermissions(workspaceId);\n // Fail-closed: strict `boolean`, never `undefined`.\n const allowed: boolean = isLoaded ? permissions.has(permissionKey) : false;\n return { allowed, isLoaded };\n}\n","/**\n * State-nonce storage for the auth-surface round-trip (CSRF / replay protection).\n *\n * The SDK MUST allow more than one in-flight sign-in flow per tab (e.g. user\n * opens sign-in in a popup while another tab races a refresh) without one\n * nonce overwriting another. We achieve this by storing a JSON map keyed by\n * `flowId` under the `ub.auth.state` `sessionStorage` key.\n *\n * Security note: `sessionStorage` writes here are the ONLY storage writes in\n * the SDK. They carry a random nonce, NOT credentials — S-1 invariant\n * (memory-only credentials) is preserved.\n */\n\nconst STORAGE_KEY = \"ub.auth.state\";\nconst MAX_FLOW_AGE_MS = 10 * 60 * 1000;\n\ntype StoredFlow = {\n nonce: string;\n createdAt: number;\n};\n\ntype StoredMap = Record<string, StoredFlow>;\n\nfunction getStorage(): Storage | null {\n if (typeof window === \"undefined\") return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\nfunction readMap(storage: Storage): StoredMap {\n try {\n const raw = storage.getItem(STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (typeof parsed !== \"object\" || parsed === null) return {};\n const out: StoredMap = {};\n for (const [k, v] of Object.entries(parsed as Record<string, unknown>)) {\n if (typeof v !== \"object\" || v === null) continue;\n const flow = v as Partial<StoredFlow>;\n if (typeof flow.nonce === \"string\" && typeof flow.createdAt === \"number\") {\n out[k] = { nonce: flow.nonce, createdAt: flow.createdAt };\n }\n }\n return out;\n } catch {\n return {};\n }\n}\n\nfunction writeMap(storage: Storage, map: StoredMap): void {\n try {\n storage.setItem(STORAGE_KEY, JSON.stringify(map));\n } catch {\n // Quota errors / disabled storage — silently ignore; the redirect will\n // still proceed but state validation will fail. Better UX than crashing.\n }\n}\n\nfunction pruneStale(map: StoredMap, now: number): StoredMap {\n const out: StoredMap = {};\n for (const [k, v] of Object.entries(map)) {\n if (now - v.createdAt <= MAX_FLOW_AGE_MS) out[k] = v;\n }\n return out;\n}\n\nfunction generateRandom(): string {\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.randomUUID === \"function\"\n ) {\n return globalThis.crypto.randomUUID();\n }\n if (\n typeof globalThis.crypto !== \"undefined\" &&\n typeof globalThis.crypto.getRandomValues === \"function\"\n ) {\n const buf = new Uint8Array(16);\n globalThis.crypto.getRandomValues(buf);\n return Array.from(buf, (b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n }\n // Last resort — non-crypto. Should never be reached in supported browsers.\n return `${Date.now().toString(16)}-${Math.random().toString(16).slice(2)}`;\n}\n\nexport type NonceHandle = {\n flowId: string;\n nonce: string;\n};\n\nexport function issueNonce(): NonceHandle {\n const flowId = generateRandom();\n const nonce = generateRandom();\n const storage = getStorage();\n if (storage) {\n const map = pruneStale(readMap(storage), Date.now());\n map[flowId] = { nonce, createdAt: Date.now() };\n writeMap(storage, map);\n }\n return { flowId, nonce };\n}\n\n/**\n * Look up the nonce for the given flowId. Single-use: the entry is removed\n * after retrieval, regardless of the comparison outcome.\n */\nexport function consumeNonce(flowId: string): string | null {\n const storage = getStorage();\n if (!storage) return null;\n const map = pruneStale(readMap(storage), Date.now());\n const entry = map[flowId];\n if (!entry) {\n writeMap(storage, map);\n return null;\n }\n delete map[flowId];\n writeMap(storage, map);\n return entry.nonce;\n}\n\n/** Test-only: clear all stored flows. */\nexport function _clearAllNonces(): void {\n const storage = getStorage();\n if (!storage) return;\n try {\n storage.removeItem(STORAGE_KEY);\n } catch {\n // ignore\n }\n}\n","import { useCallback, useContext } from \"react\";\nimport { UserboxContext } from \"./context\";\nimport { assertValidReturnTo, buildAuthSurfaceUrl } from \"../core/redirect\";\nimport { issueNonce } from \"../core/state-nonce\";\nimport { misconfigured } from \"../core/errors\";\n\nexport type SignInOptions = {\n returnTo?: string;\n};\n\nexport function useSignIn(): { signIn: (opts?: SignInOptions) => void } {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"useSignIn() must be called inside a <UserboxProvider>\",\n );\n }\n\n const signIn = useCallback(\n (opts?: SignInOptions) => {\n const returnTo =\n opts?.returnTo ??\n (typeof window !== \"undefined\" ? window.location.href : \"/\");\n assertValidReturnTo(returnTo);\n const { flowId, nonce } = issueNonce();\n const url = buildAuthSurfaceUrl({\n tenantSlug: ctx.config.tenantSlug,\n baseUrl: ctx.config.baseUrl,\n baseAuthUrl: ctx.config.baseAuthUrl,\n path: \"/login\",\n returnTo,\n state: nonce,\n flowId,\n });\n ctx.navigate(url);\n },\n [ctx],\n );\n\n return { signIn };\n}\n","import { useCallback, useContext } from \"react\";\nimport { UserboxContext } from \"./context\";\nimport { assertValidReturnTo, buildAuthSurfaceUrl } from \"../core/redirect\";\nimport { issueNonce } from \"../core/state-nonce\";\nimport { misconfigured } from \"../core/errors\";\nimport type { SignInOptions } from \"./useSignIn\";\n\nexport function useSignUp(): { signUp: (opts?: SignInOptions) => void } {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"useSignUp() must be called inside a <UserboxProvider>\",\n );\n }\n\n const signUp = useCallback(\n (opts?: SignInOptions) => {\n const returnTo =\n opts?.returnTo ??\n (typeof window !== \"undefined\" ? window.location.href : \"/\");\n assertValidReturnTo(returnTo);\n const { flowId, nonce } = issueNonce();\n const url = buildAuthSurfaceUrl({\n tenantSlug: ctx.config.tenantSlug,\n baseUrl: ctx.config.baseUrl,\n baseAuthUrl: ctx.config.baseAuthUrl,\n path: \"/signup\",\n returnTo,\n state: nonce,\n flowId,\n });\n ctx.navigate(url);\n },\n [ctx],\n );\n\n return { signUp };\n}\n","import { useCallback, useContext } from \"react\";\nimport { UserboxContext } from \"./context\";\nimport { misconfigured } from \"../core/errors\";\n\nexport type SignOutOptions = {\n redirectUri?: string;\n};\n\nexport function useSignOut(): { signOut: (opts?: SignOutOptions) => Promise<void> } {\n const ctx = useContext(UserboxContext);\n if (!ctx) {\n throw misconfigured(\n \"tenantSlug\",\n \"useSignOut() must be called inside a <UserboxProvider>\",\n );\n }\n const signOut = useCallback(\n async (opts?: SignOutOptions) => {\n await ctx.signOut(opts?.redirectUri);\n },\n [ctx],\n );\n return { signOut };\n}\n","import { useContext, useEffect, useRef } from \"react\";\nimport * as React from \"react\";\nimport { UserboxContext, type PublicSession } from \"./context\";\nimport { authFailed, missingCredentials, type UserboxError } from \"../core/errors\";\nimport { decodeJwtClaims } from \"../core/token\";\nimport { consumeNonce } from \"../core/state-nonce\";\nimport { request } from \"../core/http\";\nimport type { Session } from \"../core/refresh-engine\";\n\nexport type UserboxCallbackProps = {\n onSuccess?: (session: PublicSession) => void;\n onError?: (err: UserboxError) => void;\n};\n\nfunction parseHash(hash: string): Record<string, string> {\n const out: Record<string, string> = {};\n const raw = hash.startsWith(\"#\") ? hash.slice(1) : hash;\n if (!raw) return out;\n for (const piece of raw.split(\"&\")) {\n const [k, v] = piece.split(\"=\");\n if (!k) continue;\n out[decodeURIComponent(k)] = v ? decodeURIComponent(v) : \"\";\n }\n return out;\n}\n\nexport const UserboxCallback: React.FC<UserboxCallbackProps> = ({\n onSuccess,\n onError,\n}) => {\n const ctx = useContext(UserboxContext);\n const ranRef = useRef(false);\n\n useEffect(() => {\n if (ranRef.current) return;\n ranRef.current = true;\n if (!ctx) return;\n if (typeof window === \"undefined\") return;\n\n const hash = window.location.hash;\n const params = parseHash(hash);\n\n if (!hash || Object.keys(params).length === 0) {\n // Spec edge case: re-mount with no fragment → silent no-op.\n return;\n }\n\n // Strip the fragment BEFORE invoking onSuccess so the next render sees a\n // clean URL.\n const stripFragment = (): void => {\n const { pathname, search } = window.location;\n try {\n window.history.replaceState(null, \"\", pathname + search);\n } catch {\n // ignore\n }\n };\n\n if (params.error) {\n stripFragment();\n const err = authFailed(\n params.error_description || params.error,\n 400,\n params.error,\n );\n onError?.(err);\n return;\n }\n\n const accessToken = params.access_token;\n const refreshToken = params.refresh_token;\n const expiresIn = Number.parseInt(params.expires_in ?? \"0\", 10);\n const refreshExpiresIn = Number.parseInt(\n params.refresh_expires_in ?? params.expires_in ?? \"0\",\n 10,\n );\n const state = params.state;\n const flowId = params.flow_id;\n\n if (!accessToken || !refreshToken || !expiresIn) {\n onError?.(missingCredentials(\"Callback URL missing access_token / refresh_token / expires_in\"));\n return;\n }\n\n if (!flowId || !state) {\n stripFragment();\n onError?.(authFailed(\"Callback URL missing state/flow_id\", 400, \"STATE_MISSING\"));\n return;\n }\n\n const expectedNonce = consumeNonce(flowId);\n if (expectedNonce === null || expectedNonce !== state) {\n stripFragment();\n onError?.(authFailed(\"Callback state mismatch — possible CSRF\", 400, \"STATE_MISMATCH\"));\n return;\n }\n\n let claims;\n try {\n claims = decodeJwtClaims(accessToken);\n } catch (e) {\n stripFragment();\n onError?.(e as UserboxError);\n return;\n }\n\n const session: Session = {\n accessToken,\n accessTokenExpiresAt: Date.now() + expiresIn * 1000,\n refreshExpiresAt: Date.now() + refreshExpiresIn * 1000,\n user: {\n id: claims.sub,\n tenantId: claims.tid,\n email: null,\n fullName: null,\n },\n };\n\n // For cookie storage: hand the refresh token to the tenant backend to set\n // the httpOnly cookie. Fire-and-forget; failure surfaces via onError.\n if (ctx.config.tokenStorage === \"cookie\") {\n void (async () => {\n const res = await request<unknown>({\n url: ctx.config.refreshUrl,\n method: \"POST\",\n tenantSlug: ctx.config.tenantSlug,\n body: { action: \"init\", refresh_token: refreshToken },\n credentials: \"include\",\n errorKind: \"auth\",\n });\n if (!res.ok) onError?.(res.error);\n })();\n }\n\n ctx.adoptCallbackSession(session, refreshToken);\n stripFragment();\n onSuccess?.({ user: session.user });\n }, [ctx, onError, onSuccess]);\n\n return null;\n};\n"]}
|
package/dist/nextjs.d.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Userbox error tagged-union. Used by every layer of the SDK as the single
|
|
5
|
+
* error shape callers may inspect. Tokens (access / refresh) MUST NEVER appear
|
|
6
|
+
* in any `message` produced by these constructors — security invariant S-2.
|
|
7
|
+
*/
|
|
8
|
+
type MisconfiguredField = "tenantSlug" | "baseUrl" | "tokenStorage" | "refreshUrl" | "callbackUrl";
|
|
9
|
+
type UserboxError = {
|
|
10
|
+
kind: "missing_credentials";
|
|
11
|
+
message: string;
|
|
12
|
+
requestId?: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "auth_failed";
|
|
15
|
+
message: string;
|
|
16
|
+
httpStatus: number;
|
|
17
|
+
code: string;
|
|
18
|
+
requestId?: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: "refresh_failed";
|
|
21
|
+
message: string;
|
|
22
|
+
httpStatus: number;
|
|
23
|
+
code: string;
|
|
24
|
+
requestId?: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "permission_fetch_failed";
|
|
27
|
+
message: string;
|
|
28
|
+
httpStatus: number;
|
|
29
|
+
code: string;
|
|
30
|
+
workspaceId: string;
|
|
31
|
+
requestId?: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "network";
|
|
34
|
+
message: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "misconfigured";
|
|
37
|
+
message: string;
|
|
38
|
+
field: MisconfiguredField;
|
|
39
|
+
};
|
|
40
|
+
/** Type guard verifying a value is a `UserboxError`. */
|
|
41
|
+
declare function isUserboxError(value: unknown): value is UserboxError;
|
|
42
|
+
|
|
43
|
+
type MiddlewareConfig = {
|
|
44
|
+
tenantSlug: string;
|
|
45
|
+
baseUrl: string;
|
|
46
|
+
refreshCookieName?: string;
|
|
47
|
+
accessTokenHeader?: string;
|
|
48
|
+
cookieDomain?: string;
|
|
49
|
+
cookieSecure?: boolean;
|
|
50
|
+
cookieSameSite?: "lax" | "strict" | "none";
|
|
51
|
+
matcher?: string[];
|
|
52
|
+
onError?: (err: UserboxError, req: NextRequest) => void;
|
|
53
|
+
};
|
|
54
|
+
declare function userboxMiddleware(config: MiddlewareConfig): (req: NextRequest) => Promise<NextResponse>;
|
|
55
|
+
|
|
56
|
+
type SessionUser = {
|
|
57
|
+
id: string;
|
|
58
|
+
tenantId: string;
|
|
59
|
+
email: string | null;
|
|
60
|
+
fullName: string | null;
|
|
61
|
+
};
|
|
62
|
+
type Session = {
|
|
63
|
+
accessToken: string;
|
|
64
|
+
accessTokenExpiresAt: number;
|
|
65
|
+
refreshExpiresAt: number;
|
|
66
|
+
user: SessionUser;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Reads the access token forwarded by `userboxMiddleware` from the request
|
|
71
|
+
* headers and returns the full session. Returns `null` if the header is absent
|
|
72
|
+
* (no signed-in user for this request).
|
|
73
|
+
*
|
|
74
|
+
* **SECURITY**: the returned `ServerSession` contains the bearer `accessToken`.
|
|
75
|
+
* This object MUST NOT be passed as a prop to a Client Component — Next.js
|
|
76
|
+
* will serialise the token into the client bundle and defeat the
|
|
77
|
+
* memory-only-storage guarantee. Pass `session.user` instead.
|
|
78
|
+
*/
|
|
79
|
+
declare function getSession(): Promise<Session | null>;
|
|
80
|
+
|
|
81
|
+
type PublicSession = {
|
|
82
|
+
user: SessionUser;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { type MiddlewareConfig, type PublicSession, type Session as ServerSession, type SessionUser, type UserboxError, getSession, isUserboxError, userboxMiddleware };
|
package/dist/nextjs.js
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server.js';
|
|
2
|
+
import { headers } from 'next/headers.js';
|
|
3
|
+
|
|
4
|
+
// src/nextjs/middleware.ts
|
|
5
|
+
|
|
6
|
+
// src/core/errors.ts
|
|
7
|
+
var ERROR_KINDS = /* @__PURE__ */ new Set([
|
|
8
|
+
"missing_credentials",
|
|
9
|
+
"auth_failed",
|
|
10
|
+
"refresh_failed",
|
|
11
|
+
"permission_fetch_failed",
|
|
12
|
+
"network",
|
|
13
|
+
"misconfigured"
|
|
14
|
+
]);
|
|
15
|
+
function isUserboxError(value) {
|
|
16
|
+
if (typeof value !== "object" || value === null) return false;
|
|
17
|
+
const candidate = value;
|
|
18
|
+
return typeof candidate.kind === "string" && ERROR_KINDS.has(candidate.kind) && typeof candidate.message === "string";
|
|
19
|
+
}
|
|
20
|
+
function authFailed(message, httpStatus, code, requestId) {
|
|
21
|
+
return requestId ? { kind: "auth_failed", message, httpStatus, code, requestId } : { kind: "auth_failed", message, httpStatus, code };
|
|
22
|
+
}
|
|
23
|
+
function refreshFailed(message, httpStatus, code, requestId) {
|
|
24
|
+
return requestId ? { kind: "refresh_failed", message, httpStatus, code, requestId } : { kind: "refresh_failed", message, httpStatus, code };
|
|
25
|
+
}
|
|
26
|
+
function permissionFetchFailed(message, httpStatus, code, workspaceId, requestId) {
|
|
27
|
+
return requestId ? {
|
|
28
|
+
kind: "permission_fetch_failed",
|
|
29
|
+
message,
|
|
30
|
+
httpStatus,
|
|
31
|
+
code,
|
|
32
|
+
workspaceId,
|
|
33
|
+
requestId
|
|
34
|
+
} : { kind: "permission_fetch_failed", message, httpStatus, code, workspaceId };
|
|
35
|
+
}
|
|
36
|
+
function networkError(message) {
|
|
37
|
+
return { kind: "network", message };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/core/http.ts
|
|
41
|
+
async function request(input) {
|
|
42
|
+
const headers2 = {
|
|
43
|
+
"X-Tenant-ID": input.tenantSlug,
|
|
44
|
+
Accept: "application/json",
|
|
45
|
+
...input.headers
|
|
46
|
+
};
|
|
47
|
+
if (input.accessToken) {
|
|
48
|
+
headers2.Authorization = `Bearer ${input.accessToken}`;
|
|
49
|
+
}
|
|
50
|
+
const init = {
|
|
51
|
+
method: input.method,
|
|
52
|
+
headers: headers2
|
|
53
|
+
};
|
|
54
|
+
if (input.body !== void 0) {
|
|
55
|
+
headers2["Content-Type"] = "application/json";
|
|
56
|
+
init.body = JSON.stringify(input.body);
|
|
57
|
+
}
|
|
58
|
+
if (input.credentials !== void 0) {
|
|
59
|
+
init.credentials = input.credentials;
|
|
60
|
+
}
|
|
61
|
+
if (input.signal) init.signal = input.signal;
|
|
62
|
+
let response;
|
|
63
|
+
try {
|
|
64
|
+
response = await fetch(input.url, init);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
error: networkError(humaniseNetworkError(err))
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const requestId = response.headers.get("X-Request-ID") ?? void 0;
|
|
72
|
+
if (response.status === 204) {
|
|
73
|
+
return { ok: true, data: void 0, requestId };
|
|
74
|
+
}
|
|
75
|
+
let parsed = null;
|
|
76
|
+
const text = await response.text();
|
|
77
|
+
if (text.length > 0) {
|
|
78
|
+
try {
|
|
79
|
+
parsed = JSON.parse(text);
|
|
80
|
+
} catch {
|
|
81
|
+
parsed = null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
if (response.ok) {
|
|
85
|
+
return { ok: true, data: parsed, requestId };
|
|
86
|
+
}
|
|
87
|
+
const shape = parsed ?? {};
|
|
88
|
+
const code = typeof shape.code === "string" ? shape.code : "UNKNOWN";
|
|
89
|
+
const message = typeof shape.error === "string" ? shape.error : `HTTP ${response.status}`;
|
|
90
|
+
const error = mapServerError(input, response.status, code, message, requestId);
|
|
91
|
+
return { ok: false, error };
|
|
92
|
+
}
|
|
93
|
+
function mapServerError(input, status, code, message, requestId) {
|
|
94
|
+
switch (input.errorKind) {
|
|
95
|
+
case "refresh":
|
|
96
|
+
return refreshFailed(message, status, code, requestId);
|
|
97
|
+
case "permissions":
|
|
98
|
+
return permissionFetchFailed(
|
|
99
|
+
message,
|
|
100
|
+
status,
|
|
101
|
+
code,
|
|
102
|
+
input.workspaceId ?? "",
|
|
103
|
+
requestId
|
|
104
|
+
);
|
|
105
|
+
case "auth":
|
|
106
|
+
default:
|
|
107
|
+
return authFailed(message, status, code, requestId);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function humaniseNetworkError(err) {
|
|
111
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
112
|
+
return "Request aborted";
|
|
113
|
+
}
|
|
114
|
+
if (err instanceof Error) return err.message || "Network error";
|
|
115
|
+
return "Network error";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/nextjs/middleware.ts
|
|
119
|
+
var DEFAULT_REFRESH_COOKIE_NAME = "ub_refresh";
|
|
120
|
+
var DEFAULT_ACCESS_TOKEN_HEADER = "x-userbox-access-token";
|
|
121
|
+
var PERMANENT_STATUSES = /* @__PURE__ */ new Set([400, 401, 403]);
|
|
122
|
+
var warnedNoCookie = false;
|
|
123
|
+
function userboxMiddleware(config) {
|
|
124
|
+
const refreshCookieName = config.refreshCookieName ?? DEFAULT_REFRESH_COOKIE_NAME;
|
|
125
|
+
const accessTokenHeader = config.accessTokenHeader ?? DEFAULT_ACCESS_TOKEN_HEADER;
|
|
126
|
+
const cookieSecure = config.cookieSecure ?? process.env.NODE_ENV !== "development";
|
|
127
|
+
const sameSite = config.cookieSameSite ?? "lax";
|
|
128
|
+
return async function middleware(req) {
|
|
129
|
+
const cookie = req.cookies.get(refreshCookieName);
|
|
130
|
+
if (!cookie || !cookie.value) {
|
|
131
|
+
if (!warnedNoCookie) {
|
|
132
|
+
warnedNoCookie = true;
|
|
133
|
+
console.warn(
|
|
134
|
+
"[@userbox/react] userboxMiddleware: no refresh cookie present on this request. If the app uses tokenStorage: 'memory', the middleware should not be installed."
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return NextResponse.next();
|
|
138
|
+
}
|
|
139
|
+
const res = await request({
|
|
140
|
+
url: `${config.baseUrl}/v1/auth/refresh`,
|
|
141
|
+
method: "POST",
|
|
142
|
+
tenantSlug: config.tenantSlug,
|
|
143
|
+
body: { refresh_token: cookie.value },
|
|
144
|
+
errorKind: "refresh"
|
|
145
|
+
});
|
|
146
|
+
if (!res.ok) {
|
|
147
|
+
const err = res.error;
|
|
148
|
+
const isPermanent = (err.kind === "refresh_failed" || err.kind === "auth_failed") && PERMANENT_STATUSES.has(err.httpStatus);
|
|
149
|
+
const response2 = NextResponse.next();
|
|
150
|
+
if (isPermanent) {
|
|
151
|
+
response2.cookies.delete(refreshCookieName);
|
|
152
|
+
config.onError?.(err, req);
|
|
153
|
+
} else {
|
|
154
|
+
config.onError?.(err, req);
|
|
155
|
+
}
|
|
156
|
+
return response2;
|
|
157
|
+
}
|
|
158
|
+
const data = res.data;
|
|
159
|
+
const accessToken = typeof data.access_token === "string" ? data.access_token : "";
|
|
160
|
+
const newRefresh = typeof data.refresh_token === "string" ? data.refresh_token : "";
|
|
161
|
+
const expiresIn = typeof data.expires_in === "number" ? data.expires_in : 0;
|
|
162
|
+
const refreshExpiresIn = typeof data.refresh_expires_in === "number" ? data.refresh_expires_in : expiresIn;
|
|
163
|
+
if (!accessToken || !newRefresh || !expiresIn) {
|
|
164
|
+
const response2 = NextResponse.next();
|
|
165
|
+
config.onError?.(
|
|
166
|
+
networkError("Refresh response missing fields"),
|
|
167
|
+
req
|
|
168
|
+
);
|
|
169
|
+
return response2;
|
|
170
|
+
}
|
|
171
|
+
const requestHeaders = new Headers(req.headers);
|
|
172
|
+
requestHeaders.set(accessTokenHeader, accessToken);
|
|
173
|
+
const response = NextResponse.next({
|
|
174
|
+
request: { headers: requestHeaders }
|
|
175
|
+
});
|
|
176
|
+
response.cookies.set(refreshCookieName, newRefresh, {
|
|
177
|
+
httpOnly: true,
|
|
178
|
+
secure: cookieSecure,
|
|
179
|
+
sameSite,
|
|
180
|
+
...config.cookieDomain ? { domain: config.cookieDomain } : {},
|
|
181
|
+
maxAge: refreshExpiresIn,
|
|
182
|
+
path: "/"
|
|
183
|
+
});
|
|
184
|
+
return response;
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// src/core/token.ts
|
|
189
|
+
function base64UrlDecode(input) {
|
|
190
|
+
let s = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
191
|
+
while (s.length % 4 !== 0) s += "=";
|
|
192
|
+
if (typeof atob === "function") return atob(s);
|
|
193
|
+
return Buffer.from(s, "base64").toString("binary");
|
|
194
|
+
}
|
|
195
|
+
function decodeJwtClaims(token) {
|
|
196
|
+
const parts = token.split(".");
|
|
197
|
+
if (parts.length !== 3) {
|
|
198
|
+
throw asAuthFailed("Malformed JWT");
|
|
199
|
+
}
|
|
200
|
+
const payload = parts[1];
|
|
201
|
+
if (!payload) throw asAuthFailed("Malformed JWT payload");
|
|
202
|
+
let json;
|
|
203
|
+
try {
|
|
204
|
+
json = base64UrlDecode(payload);
|
|
205
|
+
} catch {
|
|
206
|
+
throw asAuthFailed("Malformed JWT payload");
|
|
207
|
+
}
|
|
208
|
+
let obj;
|
|
209
|
+
try {
|
|
210
|
+
obj = JSON.parse(json);
|
|
211
|
+
} catch {
|
|
212
|
+
throw asAuthFailed("Malformed JWT payload");
|
|
213
|
+
}
|
|
214
|
+
if (typeof obj !== "object" || obj === null) {
|
|
215
|
+
throw asAuthFailed("Malformed JWT payload");
|
|
216
|
+
}
|
|
217
|
+
const claims = obj;
|
|
218
|
+
const sub = typeof claims.sub === "string" ? claims.sub : null;
|
|
219
|
+
const tid = typeof claims.tid === "string" ? claims.tid : null;
|
|
220
|
+
const exp = typeof claims.exp === "number" ? claims.exp : null;
|
|
221
|
+
const jti = typeof claims.jti === "string" ? claims.jti : void 0;
|
|
222
|
+
if (!sub || !tid || exp === null) {
|
|
223
|
+
throw asAuthFailed("JWT missing required claims (sub/tid/exp)");
|
|
224
|
+
}
|
|
225
|
+
return jti ? { sub, tid, exp, jti } : { sub, tid, exp };
|
|
226
|
+
}
|
|
227
|
+
function asAuthFailed(message) {
|
|
228
|
+
return authFailed(message, 401, "MALFORMED_JWT");
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/nextjs/server.ts
|
|
232
|
+
async function getSession() {
|
|
233
|
+
const hdrs = await headers();
|
|
234
|
+
const accessTokenHeader = hdrs.get("x-userbox-access-token") ?? hdrs.get("X-Userbox-Access-Token");
|
|
235
|
+
if (!accessTokenHeader) return null;
|
|
236
|
+
let claims;
|
|
237
|
+
try {
|
|
238
|
+
claims = decodeJwtClaims(accessTokenHeader);
|
|
239
|
+
} catch {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
accessToken: accessTokenHeader,
|
|
244
|
+
accessTokenExpiresAt: claims.exp * 1e3,
|
|
245
|
+
refreshExpiresAt: 0,
|
|
246
|
+
user: {
|
|
247
|
+
id: claims.sub,
|
|
248
|
+
tenantId: claims.tid,
|
|
249
|
+
email: null,
|
|
250
|
+
fullName: null
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export { getSession, isUserboxError, userboxMiddleware };
|
|
256
|
+
//# sourceMappingURL=nextjs.js.map
|
|
257
|
+
//# sourceMappingURL=nextjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/errors.ts","../src/core/http.ts","../src/nextjs/middleware.ts","../src/core/token.ts","../src/nextjs/server.ts"],"names":["headers","response"],"mappings":"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","file":"nextjs.js","sourcesContent":["/**\n * Userbox error tagged-union. Used by every layer of the SDK as the single\n * error shape callers may inspect. Tokens (access / refresh) MUST NEVER appear\n * in any `message` produced by these constructors — security invariant S-2.\n */\n\nexport type MisconfiguredField =\n | \"tenantSlug\"\n | \"baseUrl\"\n | \"tokenStorage\"\n | \"refreshUrl\"\n | \"callbackUrl\";\n\nexport type UserboxError =\n | {\n kind: \"missing_credentials\";\n message: string;\n requestId?: string;\n }\n | {\n kind: \"auth_failed\";\n message: string;\n httpStatus: number;\n code: string;\n requestId?: string;\n }\n | {\n kind: \"refresh_failed\";\n message: string;\n httpStatus: number;\n code: string;\n requestId?: string;\n }\n | {\n kind: \"permission_fetch_failed\";\n message: string;\n httpStatus: number;\n code: string;\n workspaceId: string;\n requestId?: string;\n }\n | { kind: \"network\"; message: string }\n | { kind: \"misconfigured\"; message: string; field: MisconfiguredField };\n\nconst ERROR_KINDS: ReadonlySet<string> = new Set([\n \"missing_credentials\",\n \"auth_failed\",\n \"refresh_failed\",\n \"permission_fetch_failed\",\n \"network\",\n \"misconfigured\",\n]);\n\n/** Type guard verifying a value is a `UserboxError`. */\nexport function isUserboxError(value: unknown): value is UserboxError {\n if (typeof value !== \"object\" || value === null) return false;\n const candidate = value as { kind?: unknown; message?: unknown };\n return (\n typeof candidate.kind === \"string\" &&\n ERROR_KINDS.has(candidate.kind) &&\n typeof candidate.message === \"string\"\n );\n}\n\nexport function missingCredentials(message: string, requestId?: string): UserboxError {\n return requestId\n ? { kind: \"missing_credentials\", message, requestId }\n : { kind: \"missing_credentials\", message };\n}\n\nexport function authFailed(\n message: string,\n httpStatus: number,\n code: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? { kind: \"auth_failed\", message, httpStatus, code, requestId }\n : { kind: \"auth_failed\", message, httpStatus, code };\n}\n\nexport function refreshFailed(\n message: string,\n httpStatus: number,\n code: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? { kind: \"refresh_failed\", message, httpStatus, code, requestId }\n : { kind: \"refresh_failed\", message, httpStatus, code };\n}\n\nexport function permissionFetchFailed(\n message: string,\n httpStatus: number,\n code: string,\n workspaceId: string,\n requestId?: string,\n): UserboxError {\n return requestId\n ? {\n kind: \"permission_fetch_failed\",\n message,\n httpStatus,\n code,\n workspaceId,\n requestId,\n }\n : { kind: \"permission_fetch_failed\", message, httpStatus, code, workspaceId };\n}\n\nexport function networkError(message: string): UserboxError {\n return { kind: \"network\", message };\n}\n\nexport function misconfigured(field: MisconfiguredField, message: string): UserboxError {\n return { kind: \"misconfigured\", message, field };\n}\n","import {\n authFailed,\n networkError,\n refreshFailed,\n permissionFetchFailed,\n type UserboxError,\n} from \"./errors\";\n\nexport type HttpRequestInput = {\n url: string;\n method?: \"GET\" | \"POST\" | \"DELETE\" | \"PUT\" | \"PATCH\";\n tenantSlug: string;\n accessToken?: string | null;\n body?: unknown;\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n signal?: AbortSignal;\n /**\n * Maps a non-2xx response to the right `UserboxError` kind. Each caller\n * names its own context (refresh / permissions / etc.) so the error surface\n * stays meaningful.\n */\n errorKind: \"auth\" | \"refresh\" | \"permissions\";\n workspaceId?: string;\n};\n\nexport type HttpResponse<T> =\n | { ok: true; data: T; requestId?: string }\n | { ok: false; error: UserboxError };\n\ntype ServerErrorShape = {\n code?: unknown;\n error?: unknown;\n};\n\nexport async function request<T>(input: HttpRequestInput): Promise<HttpResponse<T>> {\n const headers: Record<string, string> = {\n \"X-Tenant-ID\": input.tenantSlug,\n Accept: \"application/json\",\n ...input.headers,\n };\n if (input.accessToken) {\n headers.Authorization = `Bearer ${input.accessToken}`;\n }\n const init: RequestInit = {\n method: input.method ?? \"GET\",\n headers,\n };\n if (input.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n init.body = JSON.stringify(input.body);\n }\n if (input.credentials !== undefined) {\n init.credentials = input.credentials;\n }\n if (input.signal) init.signal = input.signal;\n\n let response: Response;\n try {\n response = await fetch(input.url, init);\n } catch (err) {\n return {\n ok: false,\n error: networkError(humaniseNetworkError(err)),\n };\n }\n\n const requestId = response.headers.get(\"X-Request-ID\") ?? undefined;\n\n if (response.status === 204) {\n return { ok: true, data: undefined as T, requestId };\n }\n\n let parsed: unknown = null;\n const text = await response.text();\n if (text.length > 0) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = null;\n }\n }\n\n if (response.ok) {\n return { ok: true, data: parsed as T, requestId };\n }\n\n const shape = (parsed ?? {}) as ServerErrorShape;\n const code = typeof shape.code === \"string\" ? shape.code : \"UNKNOWN\";\n const message =\n typeof shape.error === \"string\"\n ? shape.error\n : `HTTP ${response.status}`;\n const error = mapServerError(input, response.status, code, message, requestId);\n return { ok: false, error };\n}\n\nfunction mapServerError(\n input: HttpRequestInput,\n status: number,\n code: string,\n message: string,\n requestId?: string,\n): UserboxError {\n switch (input.errorKind) {\n case \"refresh\":\n return refreshFailed(message, status, code, requestId);\n case \"permissions\":\n return permissionFetchFailed(\n message,\n status,\n code,\n input.workspaceId ?? \"\",\n requestId,\n );\n case \"auth\":\n default:\n return authFailed(message, status, code, requestId);\n }\n}\n\nfunction humaniseNetworkError(err: unknown): string {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n return \"Request aborted\";\n }\n if (err instanceof Error) return err.message || \"Network error\";\n return \"Network error\";\n}\n","import { NextResponse, type NextRequest } from \"next/server.js\";\nimport { request } from \"../core/http\";\nimport {\n authFailed,\n networkError,\n refreshFailed,\n type UserboxError,\n} from \"../core/errors\";\n\nexport type MiddlewareConfig = {\n tenantSlug: string;\n baseUrl: string;\n refreshCookieName?: string;\n accessTokenHeader?: string;\n cookieDomain?: string;\n cookieSecure?: boolean;\n cookieSameSite?: \"lax\" | \"strict\" | \"none\";\n matcher?: string[];\n onError?: (err: UserboxError, req: NextRequest) => void;\n};\n\nconst DEFAULT_REFRESH_COOKIE_NAME = \"ub_refresh\";\nconst DEFAULT_ACCESS_TOKEN_HEADER = \"x-userbox-access-token\";\nconst PERMANENT_STATUSES: ReadonlySet<number> = new Set([400, 401, 403]);\n\nlet warnedNoCookie = false;\n\ntype RefreshHttpResponse = {\n access_token?: unknown;\n refresh_token?: unknown;\n expires_in?: unknown;\n refresh_expires_in?: unknown;\n};\n\nexport function userboxMiddleware(\n config: MiddlewareConfig,\n): (req: NextRequest) => Promise<NextResponse> {\n const refreshCookieName = config.refreshCookieName ?? DEFAULT_REFRESH_COOKIE_NAME;\n const accessTokenHeader = config.accessTokenHeader ?? DEFAULT_ACCESS_TOKEN_HEADER;\n const cookieSecure = config.cookieSecure ?? process.env.NODE_ENV !== \"development\";\n const sameSite = config.cookieSameSite ?? \"lax\";\n\n return async function middleware(req: NextRequest): Promise<NextResponse> {\n const cookie = req.cookies.get(refreshCookieName);\n if (!cookie || !cookie.value) {\n if (!warnedNoCookie) {\n warnedNoCookie = true;\n // Warn on storage-mode mismatch.\n console.warn(\n \"[@userbox/react] userboxMiddleware: no refresh cookie present on this request. \" +\n \"If the app uses tokenStorage: 'memory', the middleware should not be installed.\",\n );\n }\n return NextResponse.next();\n }\n\n const res = await request<RefreshHttpResponse>({\n url: `${config.baseUrl}/v1/auth/refresh`,\n method: \"POST\",\n tenantSlug: config.tenantSlug,\n body: { refresh_token: cookie.value },\n errorKind: \"refresh\",\n });\n\n if (!res.ok) {\n const err = res.error;\n const isPermanent =\n (err.kind === \"refresh_failed\" || err.kind === \"auth_failed\") &&\n PERMANENT_STATUSES.has(err.httpStatus);\n const response = NextResponse.next();\n if (isPermanent) {\n response.cookies.delete(refreshCookieName);\n config.onError?.(err, req);\n } else {\n // Transient — preserve the cookie; surface error; let next request retry.\n config.onError?.(err, req);\n }\n return response;\n }\n\n const data = res.data;\n const accessToken = typeof data.access_token === \"string\" ? data.access_token : \"\";\n const newRefresh = typeof data.refresh_token === \"string\" ? data.refresh_token : \"\";\n const expiresIn = typeof data.expires_in === \"number\" ? data.expires_in : 0;\n const refreshExpiresIn =\n typeof data.refresh_expires_in === \"number\"\n ? data.refresh_expires_in\n : expiresIn;\n if (!accessToken || !newRefresh || !expiresIn) {\n const response = NextResponse.next();\n config.onError?.(\n networkError(\"Refresh response missing fields\"),\n req,\n );\n return response;\n }\n\n // Forward the access token via a request header. We mutate the incoming\n // headers in a new NextResponse since req.headers is readable but tunnels\n // must be set on the response's request rewrite.\n const requestHeaders = new Headers(req.headers);\n requestHeaders.set(accessTokenHeader, accessToken);\n\n const response = NextResponse.next({\n request: { headers: requestHeaders },\n });\n\n response.cookies.set(refreshCookieName, newRefresh, {\n httpOnly: true,\n secure: cookieSecure,\n sameSite,\n ...(config.cookieDomain ? { domain: config.cookieDomain } : {}),\n maxAge: refreshExpiresIn,\n path: \"/\",\n });\n\n // refreshFailed reference suppression\n void refreshFailed; void authFailed;\n return response;\n };\n}\n","import { authFailed, type UserboxError } from \"./errors\";\n\nexport type JwtClaims = {\n sub: string;\n tid: string;\n exp: number;\n jti?: string;\n};\n\nfunction base64UrlDecode(input: string): string {\n let s = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n while (s.length % 4 !== 0) s += \"=\";\n if (typeof atob === \"function\") return atob(s);\n return Buffer.from(s, \"base64\").toString(\"binary\");\n}\n\n/**\n * Decode JWT claims without signature verification. The server is the only\n * authority on token validity; the SDK uses\n * `exp` only to drive its proactive-refresh timer.\n */\nexport function decodeJwtClaims(token: string): JwtClaims {\n const parts = token.split(\".\");\n if (parts.length !== 3) {\n throw asAuthFailed(\"Malformed JWT\");\n }\n const payload = parts[1];\n if (!payload) throw asAuthFailed(\"Malformed JWT payload\");\n\n let json: string;\n try {\n json = base64UrlDecode(payload);\n } catch {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n\n let obj: unknown;\n try {\n obj = JSON.parse(json);\n } catch {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n\n if (typeof obj !== \"object\" || obj === null) {\n throw asAuthFailed(\"Malformed JWT payload\");\n }\n const claims = obj as Record<string, unknown>;\n const sub = typeof claims.sub === \"string\" ? claims.sub : null;\n const tid = typeof claims.tid === \"string\" ? claims.tid : null;\n const exp = typeof claims.exp === \"number\" ? claims.exp : null;\n const jti = typeof claims.jti === \"string\" ? claims.jti : undefined;\n if (!sub || !tid || exp === null) {\n throw asAuthFailed(\"JWT missing required claims (sub/tid/exp)\");\n }\n return jti ? { sub, tid, exp, jti } : { sub, tid, exp };\n}\n\nfunction asAuthFailed(message: string): UserboxError {\n return authFailed(message, 401, \"MALFORMED_JWT\");\n}\n","import { headers } from \"next/headers.js\";\nimport { decodeJwtClaims } from \"../core/token\";\nimport type { Session } from \"../core/refresh-engine\";\n\n/**\n * Reads the access token forwarded by `userboxMiddleware` from the request\n * headers and returns the full session. Returns `null` if the header is absent\n * (no signed-in user for this request).\n *\n * **SECURITY**: the returned `ServerSession` contains the bearer `accessToken`.\n * This object MUST NOT be passed as a prop to a Client Component — Next.js\n * will serialise the token into the client bundle and defeat the\n * memory-only-storage guarantee. Pass `session.user` instead.\n */\nexport async function getSession(): Promise<Session | null> {\n const hdrs = await headers();\n const accessTokenHeader =\n hdrs.get(\"x-userbox-access-token\") ?? hdrs.get(\"X-Userbox-Access-Token\");\n if (!accessTokenHeader) return null;\n\n let claims;\n try {\n claims = decodeJwtClaims(accessTokenHeader);\n } catch {\n return null;\n }\n\n return {\n accessToken: accessTokenHeader,\n accessTokenExpiresAt: claims.exp * 1000,\n refreshExpiresAt: 0,\n user: {\n id: claims.sub,\n tenantId: claims.tid,\n email: null,\n fullName: null,\n },\n };\n}\n\nexport type { Session as ServerSession } from \"../core/refresh-engine\";\n"]}
|