Authentication
Stateless JWT bearer tokens. Every authenticated endpoint expects Authorization: Bearer <token>. The API issues short-lived access tokens (about an hour) alongside long-lived refresh tokens kept in secure storage, so a session can survive for weeks without ever exposing a long-lived credential to JavaScript.
Token types
| Type | Lifetime | Purpose |
|---|---|---|
access | ~1 hour | Bearer token for API calls |
refresh | ~30 days | Exchanged for new access tokens |
passport | ~60 seconds | Cross-domain SSO redirects |
password_reset | ~1 hour | Single-use password reset |
mercure | ~24 hours | Subscribe to a Stumper.gg lobby SSE topic |
The refresh token is an HttpOnly cookie scoped to /v1/gateway/refresh for browser clients — JavaScript can't read it, so XSS can't exfiltrate it. Capacitor apps don't share the browser cookie jar across domains, so they retrieve the refresh token explicitly and pass it in the request body, storing it in iOS Keychain or Android EncryptedSharedPreferences.
Login
await katforge.auth.login ('anders@example.com', 'hunter2');
// or by username
await katforge.auth.login ('anders', 'hunter2');
Wrong credentials return 401 regardless of whether the identifier exists (prevents enumeration).
Guest sessions
const session = await katforge.auth.guest ();
// Save reclaim_token for cross-device recovery
localStorage.setItem ('reclaim', session.reclaim_token);
Upgrade to a full account later:
await katforge.auth.upgrade ({ email: 'anders@example.com', password: 'hunter2' });
Token refresh
The @katforge/api handles this automatically on 401. For raw clients:
curl -X POST https://api.katforge.com/v1/gateway/refresh \
--cookie 'kat_refresh=...'
OAuth
Providers: Discord, Google, Apple, Steam,
KATFORGE.
const { url } = await katforge.auth.oauthUrl ('discord', {
redirect: 'https://stumper.gg/oauth/callback'
});
window.location.href = url;
The callback redirect carries either access_token (existing user) or needs_username=1 (new user who needs to pick a username).
Link and unlink additional providers on an existing account:
await katforge.auth.oauthLink ('google', { code, state });
await katforge.auth.oauthUnlink ('google');
Errors
| Status | Meaning |
|---|---|
401 | Missing, invalid, or expired token |
403 | Token valid but action not allowed |
422 | Validation failed (violations array in body) |
429 | Rate limited (check Retry-After header) |
All errors return { "message": "..." }. Validation errors add { "violations": [{ "propertyPath": "email", "message": "..." }] }.
The @katforge/api throws typed exceptions: AuthenticationError, ValidationError, RateLimitError, NetworkError, etc. All extend KATforgeError.