Authentication
KATforge uses stateless JWT access tokens. Every authenticated endpoint expects an Authorization: Bearer <token> header. Tokens are issued by the gateway endpoints under /v1/gateway/.
Token types
KATforge issues five distinct token types, each with its own purpose and lifetime:
| Type | Issued by | Lifetime | Purpose |
|---|---|---|---|
access | Gateway login / OAuth / guest | ~1 hour | Bearer token for normal API calls |
refresh | Gateway login / OAuth / guest | ~30 days | Exchanged for fresh access tokens |
passport | GET /v1/tokens/passport | ~60 seconds | Cross-domain SSO redirects |
password_reset | POST /v1/gateway/reset-password/request | ~1 hour | Single-use password reset |
mercure | POST /v1/games/stumper/lobbies/{code}/token | ~24 hours | Subscribe to a single Stumper lobby's SSE topic |
The refresh token is delivered as an HttpOnly cookie scoped to /v1/gateway/refresh, so browser clients never need to handle it directly. Capacitor and other non-browser clients pass it explicitly in the request body.
Email / password login
const session = await katforge.auth.login ('anders@example.com', 'hunter2');
// or by username:
const session = await katforge.auth.login ('anders', 'hunter2');
The identifier field accepts either an email or a username — detection is automatic based on the presence of @. Wrong credentials always return 401 Invalid credentials regardless of which field exists, so attackers can't enumerate accounts.
Rate limited to 10 requests per minute per IP.
Guest sessions
Guests are first-class players. They have a real Player.id, can submit results, and appear on leaderboards. The only thing they can't do is log back in from a different device — unless you save the reclaim_token returned alongside the access token.
const session = await katforge.auth.guest ();
// Save this somewhere durable so the user can recover the same player ID later
localStorage.setItem ('katforge:reclaim', session.reclaim_token);
// Later, on a different device or after reinstall
const restored = await katforge.auth.guest ({
reclaimToken: localStorage.getItem ('katforge:reclaim')
});
Guests can be upgraded to full accounts later, preserving their player ID and all game data:
await katforge.auth.upgrade ({
email: 'anders@example.com',
password: 'hunter2'
});
Refreshing tokens
Access tokens expire after about an hour. The SDK refreshes them automatically on 401 responses — you should never have to think about this. If you're calling the API directly:
# Browser: cookie sent automatically
curl -X POST https://api.katforge.com/v1/gateway/refresh \
--cookie 'kat_refresh=...'
# Capacitor / non-browser: pass it in the body
curl -X POST https://api.katforge.com/v1/gateway/refresh \
-H 'Content-Type: application/json' \
-d '{ "refresh_token": "..." }'
OAuth (Discord, Google, Apple, Steam)
See the OAuth Providers guide for the full social-login flow.
Cross-domain SSO
When the user is logged in on katforge.com and clicks through to lextris.com, you don't want them to log in again. KATforge solves this with passport tokens:
- Originating site calls
GET /v1/tokens/passportwith its access token to mint a short-lived passport. - It redirects the browser to
https://api.katforge.com/v1/gateway/customs?passport=<jwt>&redirect=https://lextris.com/play. - The customs handler validates the passport, mints a fresh access/refresh pair, sets the refresh cookie on the destination domain, and redirects the browser to
redirect.
// On katforge.com
const { passport, expires_in } = await katforge.auth.passport ();
window.location.href = `https://api.katforge.com/v1/gateway/customs`
+ `?passport=${encodeURIComponent (passport)}`
+ `&redirect=${encodeURIComponent ('https://lextris.com/play')}`;
The passport JWT is intentionally short-lived (~60 seconds) — long enough to survive a browser redirect, short enough that a leaked URL is useless. The destination must be on a KATforge-owned domain.
Logout
await katforge.auth.logout ();
Stateless JWTs cannot be invalidated server-side. Logout simply clears the refresh cookie and tells the SDK to drop its in-memory access token. The current access token will continue to work until it expires (≤1 hour). For high-stakes scenarios, rotate the user's password.
Errors
| Status | Meaning |
|---|---|
401 Unauthorized | Missing, invalid, or expired token |
403 Forbidden | Token is valid but the action requires more privilege |
429 Too Many Requests | Rate limit hit; check the Retry-After header |
See Errors for the full error format.