@urun-sh/next
v0.2.40
Published
Next.js on-ramp for uRun scoped client tokens — a fully-built token route in one import and one line.
Readme
@urun-sh/next
Next.js on-ramp for uRun scoped client tokens — never ship your org API key to the browser.
Zero-config: the entire connection story
With only URUN_API_KEY in the server env (raw pnpm dev / pnpm build,
no CLI), this is a complete uRun frontend:
// app/api/urun-token/route.ts — the entire server leg
import { createTokenRoute } from '@urun-sh/next'
export const POST = createTokenRoute()// Client code: hardcode YOUR app name, mirroring the backend's App("...").
// Sessions bind by method call — no function-name strings.
<UrunProvider app="rolling-sink">…</UrunProvider>
// const app = useApp(); const session = app.rolling_sink()<UrunProvider> (from @urun-sh/react) POSTs /api/urun-token after mount;
the route answers { token, expiresAt, orgId, gatewayUrl } — org derived from
the key, gateway the platform front door (never user configuration). Nothing
else is set anywhere: no org id, no gateway URL, no NEXT_PUBLIC_*.
createTokenRoute() mints for ANY caller by default (the public-demo
posture — a server-side warning reminds you once); pass
createTokenRoute({ authorize }) to restrict minting to your authenticated
users. All the scoping options below apply to it as well.
App Router (explicit authorization)
// app/api/urun-token/route.ts
import { urunTokenRoute } from '@urun-sh/next'
export const POST = urunTokenRoute({
authorize: (req) => Boolean(getSessionUser(req)), // required: do not mint for anonymous callers
subject: (req) => getSessionUser(req)?.id, // stable per-end-user identity
})Set the URUN_API_KEY env secret and provide an authorize callback that
returns true only for an authenticated application user/session. The route
mints short-lived tokens the browser passes to the SDK through the existing
jwt / getAccessToken auth options. Requests are rejected with 401 before
minting when authorize is omitted or returns false.
Scoping
export const POST = urunTokenRoute({
authorize: (req) => Boolean(getSessionUser(req)),
allowedFunctions: ['zimage/generate'], // only these app/functions
allowedOrigins: ['https://app.example.com'], // only from these Origins
maxSessionS: 900, // per-session length cap
expiresIn: 120, // token start-window (max 3600)
subject: (req) => getSessionUser(req)?.id, // stable per-end-user identity
})Constraints are enforced server-side at session admission. A per-request
body may only narrow these (subset of an allowlist, lower expiry, tighter
cap) — never widen, and never choose its own subject.
Token expiry gates starting sessions only: a live session rides its lease
and is never killed by token expiry; maxSessionS is the honest per-session
cap (folded with the function's own max_session_s).
Pages Router
// pages/api/urun-token.ts
import { urunTokenPagesHandler } from '@urun-sh/next'
export default urunTokenPagesHandler({
authorize: (req) => Boolean(getSessionUserFromPagesReq(req)),
})Session config — hardcode nothing
The other half of the on-ramp: urun deploy derives your frontend's
session-plane config from the deploy (gateway from your login, app id +
function from the deployed app) and writes it to frontend/.env.local.
urunSessionConfig() reads it back so the frontend hardcodes nothing:
// app/page.tsx — a Server Component
import { urunSessionConfig } from '@urun-sh/next'
import { Client } from './client'
export default function Page() {
const { baseUrl, appId, functionName } = urunSessionConfig()
return <Client baseUrl={baseUrl} appId={appId} functionName={functionName} />
}The org id and auth provider are never here — a frontend never hardcodes
its org. They are derived server-side from URUN_API_KEY when the token route
mints the token: the minted token carries the org claim, and createClientToken
returns the resolved orgId (in the { token, expiresAt, orgId } response) for
the browser to hand to <UrunProvider orgId=… jwt=… />. The gateway ignores the
auth provider for platform-minted client tokens, so no authProvider is needed.
The end state: a user sets only URUN_API_KEY and runs urun deploy — no
NEXT_PUBLIC_SESSION_* editing.
Canonical env vars (plain server env, not NEXT_PUBLIC_*): URUN_GATEWAY_URL
(default https://api.urun.sh), URUN_APP, URUN_FUNCTION.
Server SDK
The underlying mint call is createClientToken(apiKey, options) from
@urun-sh/core — plain fetch, Node-safe, framework-free.
