Skip to content

Tokens & auth

Mint a token for a tenant, then attach a browser session without putting a long-lived token in a URL.

Terminal window
import { issueNimbusToken } from '@nimbus-sh/sdk';
const token = await issueNimbusToken(env, {
tn: 'acme', // tenant (required)
sub: 'alice', // subject within the tenant
scopes: ['sandbox:use'], // grants; absent = all permitted
sid: 'job-123', // pin the token to one session
});

Tokens are HS256 JWTs signed with JWT_SECRET, using pure WebCrypto. There is no external IdP or dependency. The verifier ignores alg in the token header, as required by RFC 8725 §3.1. The server enforces scopes, tenant and session pins, and destroy permissions. Client checks are only a convenience.

The seven scopes are session:create, session:attach, session:bootstrap, session:preview, session:destroy, session:admin, sandbox:use. Nimbus mints session:bootstrap and session:preview itself. Each is single-use and grants nothing beyond its own exchange.

The default TTL is 1 hour and the maximum is 30 days. A larger value throws E_TOKEN_TTL_TOO_LARGE.

To rotate the secret, set JWT_SECRET_PREVIOUS next to the new JWT_SECRET. Verification accepts either secret, and issuing uses the primary. Drop the old secret after the longest TTL passes.

Long-lived tokens only travel in Authorization: Bearer headers. The attach exchange uses one short-lived URL token:

  1. POST /new with session:create redirects to an attach URL carrying a 90-second, single-use session:bootstrap token. The session DO consumes its jti set-if-absent. Replays get 401.
  2. The shell trades it for a sid-pinned session:attach cookie (HttpOnly, Secure, SameSite=None, Partitioned), then redirects to the clean URL. The token does not stay in history.
  3. HTML, WebSocket, and API requests all ride that cookie.
Terminal window
import { sessionAttachUrl, issueNimbusToken } from '@nimbus-sh/sdk';
const url = sessionAttachUrl('https://my-nimbus.workers.dev', 'pretty-otter-1234', token);
// → https://my-nimbus.workers.dev/s/pretty-otter-1234/?nimbus_token=eyJ…

Known limit: each browser partition holds one sid-pinned cookie. Two Nimbus iframes on one page evict each other’s cookie. Concurrent embeds need a per-session cookie design; that work is still open.

Auth failures use typed errors such as NimbusTokenExpiredError, NimbusTokenSignatureError, and NimbusAuthConfigError.