Authentication

Tokens

The JWT model — short-lived access tokens, audiences per product, the refresh flow, and JWKS.

Not verified yet

Every Norcube API accepts Bearer JWTs. The dashboard and the CLI manage the whole lifecycle for you; this page is for anyone integrating directly.

The model

Two token kinds, both RS256-signed JWTs:

TokenLifetimeWhere it lives
Access token~5 minutesSent as Authorization: Bearer <jwt> on every request. Claims include the user (sub), the organization_id, the aud list, and token_type: "access".
Refresh token30 days, renewed on every useThe session. For the web app: an HTTP-only jds_refresh_token cookie. For the CLI: stored per profile in the OS keyring, or in a file readable only by you where there is no keyring. Used only to mint new access tokens.

Access tokens are deliberately short — clients should treat 401 INVALID_TOKEN as "refresh once and retry", not as an error.

Audiences

Each product validates tokens against its own audience; a token minted for one product won't authenticate against another:

AudienceService
authThe auth service itself (user profile, organizations, IAM).
langsync-apiLangSync
snapdb-apiBackup
domainradarDomainRadar
billingBilling
prompthubPromptHub

Convenience: tokens minted for a product audience also include auth, so the same token can fetch your profile and org list alongside product calls.

Minting an org-scoped access token

The auth service exposes an OAuth2-style endpoint. The refresh token travels as the jds_refresh_token cookie (not a form field):

curl -X POST "https://auth.api.norcube.com/oauth/token" \
  -H "Cookie: jds_refresh_token=<your-refresh-token>" \
  -d grant_type=refresh_token \
  -d audience=langsync-api \
  -d organization_id=<org-uuid>

The response carries a fresh access token (and possibly a rotated refresh token — always persist the returned one). Cache the access token until shortly before expiry, then refresh.

Two things the endpoint does not do:

  • No client_credentials grant. There is no machine-to-machine client flow; the refresh token always originates from a human sign-in.
  • For unattended access, use a CI token: an organization-scoped, expiring credential exchanged for 5-minute product tokens. Its sub is the CI token's id (with principal_type: "ci_token"), not a person, and its scope claim limits it to what it was granted. There are no personal access tokens.

CLI sessions and refresh-token families

A CLI sign-in (POST /auth/cli/exchange) is recorded as a session, and the session is one refresh-token family:

  • Every refresh token the session issues, and every access token minted from them, carries the session id in a sid claim.
  • Only the newest refresh token of the family works. Each refresh replaces it; exactly one of two simultaneous refreshes of the same token succeeds, the other gets 401.
  • Presenting an older refresh token of the family means two parties hold the session's tokens, so the whole session is revoked (RFC 9700 §4.14.2). The token replaced in the last 10 seconds is the one exception: it is refused with 401, but the session survives, because that is a client retrying a request whose response it lost.
  • POST /auth/cli/sign-out with the form field refresh_token ends the session the token belongs to. It always answers 204.

A device sign-in (nrc login --device, the OAuth 2.0 Device Authorization Grant of RFC 8628) ends in exactly the same kind of session. The CLI asks POST /auth/cli/device/code for a code pair, the person approves the user code on the dashboard's /device page, and the CLI's polling of POST /auth/cli/device/token (errors authorization_pending, slow_down, access_denied, expired_token) then returns the session, once.

Web sign-in tokens carry no sid and aren't listed as sessions.

Verifying tokens (JWKS)

The signing keys are public:

GET https://auth.api.norcube.com/.well-known/jwks.json

If you're building something that receives Norcube tokens, verify RS256 signatures against this JWKS and check the aud and token_type claims — exactly what every Norcube service does.

Behaviour and edge cases

  • Wrong audience → 401. Mint a token with the audience of the API you're calling.
  • Refresh tokens can rotate. When /oauth/token returns a new refresh token, the old one is dead — store the replacement immediately (the CLI does this for you).
  • Sessions are independent. Web login and CLI login hold separate refresh tokens; signing out of one doesn't invalidate the other.
  • CLI sessions can be revoked. Each CLI sign-in is a session listed under Account › CLI sessions in the dashboard (and GET /auth/me/sessions). Revoking one stops its refresh token at once; access tokens it already minted run out within their ~5-minute lifetime. Sessions unused for 30 days expire.
  • Lost refresh token = re-login. They can't be re-issued.
  • CI tokens — the credential for CI pipelines and scripts.
  • API keys — the credential for applications.
  • CLI — the browser-handshake login that gets the CLI its tokens.

On this page