Setup · Required for entitlement reads
Tenant JWT
When a mobile/web app asks stubkit “is this user Pro?”, stubkit needs to know which user is asking and trust that the SDK isn’t lying about their identity. You do this by signing a short-lived JWT with your own private key. Stubkit verifies it against the public key you publish.
Two setup options
Option A — JWKS URL (recommended)
Host a standard JWKS endpoint at your own domain. Any OIDC/OAuth2 provider already does this:
- Supabase:
https://<project>.supabase.co/auth/v1/.well-known/jwks.json - Clerk:
https://<frontend-api>.clerk.accounts.dev/.well-known/jwks.json - Auth0:
https://<tenant>.auth0.com/.well-known/jwks.json - Firebase:
https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com - Your own Worker: publish
{ "keys": [{ "kty": "RSA", "n": "...", "e": "AQAB", "kid": "..." }] }
Configure in stubkit:
- Dashboard → your app → Settings
- Under Tenant JWT config, paste the JWKS URL.
- Leave Issuer / Audience blank unless your tokens set them — in which case fill both to tighten the check.
- Save.
Option B — Issuer + Audience with fixed secret
Only use this if you’re not running any OIDC provider. Publish your public key bytes at a URL stubkit can fetch, and set the iss / audclaims your backend will emit.
Token requirements
Every token you mint must:
- Be signed with RS256 (or ES256 if your JWKS says so).
- Include a
subclaim — your internal user id. This is what the SDK passes asuserId. - Have an
expno more than 1 hour in the future. Refresh before it expires. - If you configured issuer/audience in stubkit, match them in
iss/aud.
Example payload stubkit expects:
{
"sub": "user_abc123",
"iss": "https://auth.yourapp.com",
"aud": "stubkit",
"exp": 1770000000,
"iat": 1769996400
}Minting tokens — Node example
import jwt from 'jsonwebtoken';
import { readFileSync } from 'fs';
const privateKey = readFileSync('/run/secrets/jwt-private-key.pem');
export function mintStubkitToken(userId: string) {
return jwt.sign(
{
sub: userId,
iss: 'https://auth.yourapp.com',
aud: 'stubkit',
},
privateKey,
{ algorithm: 'RS256', expiresIn: '1h', keyid: 'key-1' }
);
}Then in the mobile/web app, your backend exposes POST /stubkit-tokenwhich returns the signed JWT. The SDK’s getAuthToken callback calls that endpoint.
SDK wiring
import { StubkitClient } from '@stubkit/js';
const stubkit = new StubkitClient({
appId: 'your-app-id',
publishableKey: 'pk_live_xxx...',
getAuthToken: async () => {
const res = await fetch('/api/stubkit-token');
const { token } = await res.json();
return token;
},
});Cache behavior
Stubkit caches JWKS fetches in KV for 10 minutes. If you rotate your signing key and ship new kid-tagged tokens, stubkit will re-fetch within 10 min automatically. For emergency rotation, restart your signing infra and rely on the cache-miss on the next request.
Common errors
401 jwt_missing— noAuthorizationheader.401 jwt_expired—expin the past; your minter TTL is too short or clock skew.401 jwt_invalid_signature—kiddoesn’t resolve to your JWKS, or the key rotated and cache is stale.401 jwt_invalid_issuer/jwt_invalid_audience— claim mismatch with what you configured.