Widgets
Widget tokens
The bearer credential that gates every @authio/widgets call. Minted from your BFF, scoped to one organization + one origin set, capped at 1 hour, revocable instantly.
What a widget token is
Widget tokens are a third, fully separate JWT-kind alongside the existing customer-tenant and platform-tenant JWTs. They embed the organization the widget reads/writes, the scope of operations the widget is allowed to perform (today, sso_connection and/or directory_sync), and the HTTPS origin(s) the token may be presented from.
For the full security posture (origin enforcement, scope checks, DB-row-backed revocation, audit emission) see Widgets security model.
Mint — POST /v1/widget-tokens
Server-only. Caller must be an owner or admin of the org or the tenant; member / viewer get 403 insufficient_role. The endpoint clamps ttl_seconds to [60, 3600] (default 1800 s / 30 minutes).
curl -X POST https://api.authio.com/v1/widget-tokens \
-H "authorization: Bearer $AUTHIO_DASHBOARD_SESSION_JWT" \
-H "x-authio-tenant: ten_acme" \
-H "content-type: application/json" \
-d '{
"organization_id": "org_acme_hq",
"scope": ["sso_connection", "directory_sync"],
"origins": ["https://app.acme.com"],
"ttl_seconds": 1800
}'Response (the JWT is shown once):
{
"id": "wtok_a1b2c3d4e5f60718293040ab",
"token": "eyJhbGciOi…",
"expires_at": "2026-05-23T22:00:00Z",
"warning": "This token is shown once. Store it server-side and ship it to the browser only at render time."
}List — GET /v1/widget-tokens
curl "https://api.authio.com/v1/widget-tokens?organization_id=org_acme_hq" \
-H "authorization: Bearer $AUTHIO_DASHBOARD_SESSION_JWT" \
-H "x-authio-tenant: ten_acme"Returns the active, non-expired, non-revoked tokens. Pass ?include_revoked=true to read the full audit history for the org.
Revoke — DELETE /v1/widget-tokens/:id
curl -X DELETE https://api.authio.com/v1/widget-tokens/wtok_a1b2c3d4e5f60718293040ab \
-H "authorization: Bearer $AUTHIO_DASHBOARD_SESSION_JWT" \
-H "x-authio-tenant: ten_acme"Sets revoked_at = now(). The next /widget/* request that presents the JWT returns 401 widget_token_revoked within milliseconds — the guard reads the DB row on every call, not just at JWT-mint.
Verifying a widget token in your own backend
Widget tokens are intended to be presented by the browser to auth-api.authio.com/widget/* directly — the @authio/widgets bundle handles the wire format and the origin header for you. If you want to verify the JWT yourself (for instance to gate a server-side rendered dashboard panel), use the standard JWKS verifier from @authio/node:
import { JwtVerifier } from "@authio/node";
const verifier = new JwtVerifier({
jwksUri: "https://auth-api.authio.com/.well-known/jwks.json",
issuer: "https://auth-api.authio.com",
});
const claims = await verifier.verify(widgetJwt);
if (claims.kind !== "widget") {
throw new Error("not a widget token");
}
// claims.tenant_id, claims.organization_id, claims.widget_scope[],
// claims.widget_origins[] are all populated./v1/sessions/*, /v1/me, or /v1/session/*. Every other Authio surface refuses kind=widget with 403 widget_token_not_allowed_here. Conversely a regular customer-session JWT cannot be presented to /widget/* — it returns 403 widget_token_required.Read next
- SSO Connection widget — install + embed walkthrough.
- Directory Sync widget — install + embed walkthrough.
- Widgets security model — every guard the widget surface enforces.
