Skip to content

Authentication

Klaxon's authentication surface is split across two binaries:

  • klaxon-auth (port 3001) is a compliant OAuth 2.1 Authorization Server with RFC 8414 discovery, RFC 7591 dynamic client registration, PKCE S256, refresh-token rotation, and RFC 7009 revocation. It also handles all IdP login flows (GitHub, Google, Apple, magic link) and account management (/auth/me, /auth/logout, /auth/api-keys).
  • klaxon-server (port 3000) hosts the protected resources. It validates bearer tokens against the shared Postgres schema via the klaxon-auth-core extractor — no cross-service RPC.

Klaxon issues two shapes of bearer token:

  • Session tokens for human users (web / mobile) — opaque, SHA-256 hashed at rest, 30-day TTL, 7-day on invite-accept.
  • API keys for MCP agents — same shape, configurable TTL. Created automatically when an admin creates an agent.

Human Login

Pick whichever IdP fits your deployment. You can enable multiple — the login screen picks up whatever's configured.

GitHub OAuth

Set these env vars on klaxon-auth:

Env varPurpose
GITHUB_CLIENT_IDOAuth app client ID
GITHUB_CLIENT_SECRETOAuth app client secret

Register the callback as https://<auth-host>/auth/oauth/github/callback. First-time signup auto-creates a personal org + user + user_identity row.

Google OAuth

Env varPurpose
GOOGLE_CLIENT_IDOAuth client ID
GOOGLE_CLIENT_SECRETOAuth client secret

Callback: https://<auth-host>/auth/oauth/google/callback.

Sign in with Apple

Apple uses ES256-signed client-secret JWTs and JWKS-verified id_tokens. You'll need:

Env varPurpose
APPLE_CLIENT_IDYour Services ID (e.g. com.klaxon.signin)
APPLE_TEAM_ID10-char Team ID from Apple Developer
APPLE_KEY_ID10-char Key ID for the .p8 private key
APPLE_PRIVATE_KEYContents of the .p8 file (including the BEGIN/END PRIVATE KEY lines)

Unlike other IdPs, Apple POSTs to the callback URL. Register https://<auth-host>/auth/oauth/apple/callback as your redirect.

Email-based passwordless login — no IdP app to register, just SMTP. User enters their email, receives a short-lived link, clicks it.

Step 1 — Request magic link:

bash
curl -X POST http://localhost:3000/auth/magic-link \
  -H "Content-Type: application/json" \
  -d '{"email": "alice@example.com", "org_id": "ORG_UUID"}'

Always returns 200 (doesn't leak whether the email exists). If SMTP is configured, sends an email with a login link. Link expires in 15 minutes, single-use.

Step 2 — Exchange token for session:

bash
curl "http://localhost:3000/auth/verify?token=base64url-token-from-email-link"

Response:

json
{
  "token": "session-bearer-token",
  "user_id": "...",
  "org_id": "..."
}

Sessions expire after 30 days.

Dev mode: When SMTP is not configured (no SMTP_HOST env var), the magic link token is logged to the console and returned in the response body. This lets you develop without an email server.

SMTP configuration:

  • SMTP_HOST — mail server hostname
  • SMTP_PORT — port (default: 587)
  • SMTP_USER / SMTP_PASS — credentials
  • SMTP_FROM — sender address (default: noreply@klaxon.sh)
  • APP_URL — base URL for the link (default: http://localhost:5173)

Dev bypass (optional)

Setting DEV_LOGIN_ENABLED=true on klaxon-auth makes /oauth/authorize auto-consent for the first-party client without prompting. Combined with the magic-link flow returning the token in the response body when SMTP is unset, this lets you develop end-to-end without any external services.

WARNING

Dev bypass accepts any email without verification. Never set this in production.

Agent API Keys

Users create API keys for their agents. Each key is bound to a specific agent entity and has a configurable TTL.

Create an API Key

bash
curl -X POST http://localhost:3000/auth/api-keys \
  -H "Authorization: Bearer SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "AGENT_UUID",
    "label": "my-agent-prod",
    "expires_in_days": 90
  }'

Response:

json
{
  "id": "KEY_UUID",
  "key": "base64url-encoded-api-key"
}

WARNING

The key is shown only once. Store it securely.

Revoke an API Key

bash
curl -X POST http://localhost:3000/auth/api-keys/revoke \
  -H "Authorization: Bearer SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"key_id": "KEY_UUID"}'

Revoked keys are soft-deleted (revoked_at timestamp) and immediately stop working.

Using Tokens

All authenticated endpoints require the Authorization header:

Authorization: Bearer <token>

The server tries the token as a session first, then as an API key. The resolved identity includes:

FieldDescription
user_idThe user who owns the session/key
org_idThe organization scope
agent_idThe agent (API keys only; null for sessions)
source"session" or "api_key"

Who Am I

bash
curl http://localhost:3000/auth/me \
  -H "Authorization: Bearer TOKEN"
json
{
  "user_id": "...",
  "org_id": "...",
  "agent_id": null,
  "source": "session"
}

Logout

bash
curl -X POST http://localhost:3000/auth/logout \
  -H "Authorization: Bearer SESSION_TOKEN"

Revokes the current session.

OAuth 2.1 for third-party clients

klaxon-auth is a compliant Authorization Server so external MCP clients can use standard OAuth flows instead of copy-pasting API keys.

  • Discovery: GET /.well-known/oauth-authorization-server (RFC 8414) and GET /.well-known/oauth-protected-resource (RFC 9728)
  • Dynamic client registration: POST /oauth/register (RFC 7591) — takes redirect_uris, returns a client_id + client_secret
  • Authorization code flow with PKCE S256: GET /oauth/authorizePOST /oauth/token. The code_verifier / code_challenge pair is mandatory — the server rejects any token exchange without it.
  • Refresh tokens rotate: each refresh returns a new refresh token and invalidates the previous one. A replay is a hard error.
  • Revocation: POST /oauth/revoke (RFC 7009) accepts either an access token or a refresh token.

The browser session cookie that underpins /oauth/authorize is HMAC-signed with OAUTH_SIGNING_KEY — rotate it and every logged-in browser session invalidates at once.

Env varPurpose
OAUTH_SIGNING_KEY32+ random bytes, openssl rand -base64 32. Signs the browser session cookie + OAuth state param.
ISSUER_URLPublic URL of the AS — the issuer field in discovery
RESOURCE_URLPublic URL of klaxon-serverWWW-Authenticate points MCP clients here