Token Types
| Token | Purpose | Lifetime | Storage |
|---|---|---|---|
| Access Token | API authorization | 1 hour (default) | Client-side (JWT) |
| Refresh Token | Obtain new access tokens | 30 days (default) | Server-side |
| Authorization Code | OAuth flow exchange | 60 seconds | Server-side, single-use |
| Session Token | Track user session | Configurable | Depends on mode |
Session Modes
FrontMCP supports two session management strategies:Stateful Sessions
Tokens stored server-side. Clients hold lightweight references.Pros:
- Silent token refresh
- Revocation without client update
- Secure token storage
- Requires shared storage (Redis)
- State management complexity
Stateless Sessions
All data embedded in JWT. No server-side storage.Pros:
- Horizontally scalable
- No shared state
- Simple architecture
- No silent refresh
- Larger token size
- Can’t revoke without expiry
Stateful Session Configuration
Stateless Session Configuration
Token Storage
tokenStorage (on local/remote auth) persists authorization codes, refresh tokens, and federated-auth sessions. Three backends are supported.
In-Memory (Development)
In-memory storage loses all data on restart. Use only for development.
SQLite (Single-Node Persistence)
@frontmcp/storage-sqlite (lazy-loaded).
Redis (Multi-Instance Persistence)
Storage Contents
| Key Pattern | Data | TTL |
|---|---|---|
{prefix}pending:{id} | Pending authorization | 10 minutes |
{prefix}code:{code} | Authorization code | 60 seconds |
{prefix}refresh:{token} | Refresh token | 30 days |
{prefix}session:{id} | Session data | Configurable |
OrchestratedTokenStore Interface
When using local or remote mode with federated authentication, FrontMCP stores provider tokens using theOrchestratedTokenStore interface.
Interface Methods
| Method | Description |
|---|---|
storeTokens(authorizationId, providerId, tokens) | Store tokens for a provider |
getTokens(authorizationId, providerId) | Retrieve tokens for a provider |
deleteTokens(authorizationId, providerId) | Delete tokens for a provider |
getProviderIds(authorizationId) | Get all provider IDs with stored tokens |
migrateTokens(fromAuthId, toAuthId) | Migrate tokens between authorization IDs |
Token Migration
ThemigrateTokens() method is used internally during federated auth completion to transfer tokens from a pending authorization ID to the final authorization ID:
migrateTokens() is called automatically during the OAuth token exchange flow when completing federated authentication.Token Lifetimes
FrontMCP uses default token lifetimes:| Token Type | Default Lifetime |
|---|---|
| Access Token | 1 hour |
| Refresh Token | 30 days |
| Authorization Code | 60 seconds |
Token Refresh Configuration
The
refresh block governs proactive refresh of upstream/downstream provider credentials (the tokens read via this.authProviders / this.orchestration.getToken), not the server’s own session token. There is no background refresher for FrontMCP’s own access token: the client refreshes its session token explicitly by calling POST /oauth/token with grant_type=refresh_token (see Token Refresh Flow). Server-issued token lifetimes are fixed at the server level, and refresh tokens are rotated on each use per OAuth 2.1 best practices.Token Refresh Flow
Refresh tokens are rotated on each use (OAuth 2.1 best practice):JWT Structure
Access tokens issued bylocal/remote mode are JWTs signed with HS256 (symmetric, JWT_SECRET). There is no kid because there is no key set to select from:
Custom Claims
In transparent/remote modes, custom claims are sourced from the upstream identity provider — configure your IdP to include the claims you need (roles, tenant ID, etc.). Inlocal mode, your authenticate() handler can return and customize the claims FrontMCP embeds in the issued token: resolve a { ok: true, sub?, claims? } result and the claims object is merged into the minted access-token payload (namespaced to avoid clobbering reserved claims like sub/exp/iss).
Either way, read the claims via this.context.authInfo?.user (raw) or this.auth.claims (typed) inside tools, resources, prompts, and agents.
Consent & Tool Authorization
Enableconsent to enforce a per-token authorized-tools claim:
How Consent Works Today
- User authenticates via the built-in login page.
/oauth/callbackrenders an interactive tool-selection screen listing the available tools (honoringgroupByApp,showDescriptions,allowSelectAll,requireSelection,customMessage,excludedTools,defaultSelectedTools).- The user’s checked tools are GET-submitted back to
/oauth/callback, embedded in the token’sconsent.selectedToolsclaim, and enforced on everytools/call— an unselected tool is rejected withTOOL_NOT_CONSENTED(JSON-RPC-32003).
Tokens minted without consent (consent disabled, or created via the test/programmatic factory) carry no
consent claim and stay all-tools-allowed. excludedTools are always available. rememberConsent (default true) persists each user’s per-client selection and reuses it on the next login, re-prompting only when a NEW tool appears; set it false to always re-show the screen.Tool-Level Scopes
Signing Secret (HS256)
local and remote modes sign the tokens they issue with HS256 — a single symmetric secret read from the JWT_SECRET environment variable. The same secret signs and verifies tokens; there is no RSA/EC key pair and no private key to manage.
Rotating the secret
RotatingJWT_SECRET immediately invalidates every token signed with the old value — connected clients must re-authenticate. There is no built-in dual-secret overlap window; schedule rotations during a maintenance window or rely on short access-token lifetimes plus refresh.
transparent mode is the asymmetric path: tokens are verified against the upstream IdP’s JWKS (providerConfig.jwksUri / jwks), not a local key. In local/remote mode, /.well-known/jwks.json does publish an auto-generated asymmetric (RS256 by default) public key for OAuth-discovery compatibility — but that key is not used to verify the FrontMCP-issued access tokens, which are HS256 and always verified with JWT_SECRET.Token Verification
Verification Flow
Verification Options
Error Responses
Token-related errors follow OAuth 2.0 error format:| Error | HTTP Status | Description |
|---|---|---|
invalid_token | 401 | Token expired, malformed, or invalid signature |
insufficient_scope | 403 | Token missing required scopes |
invalid_request | 400 | Malformed token request |
invalid_grant | 400 | Invalid authorization code or refresh token |
Example Error Response
Next Steps
Authorization Modes
Choose the right auth mode for your use case
Progressive Authorization
Implement incremental app authorization
Production Checklist
Security requirements for deployment
Remote OAuth
Connect to external identity providers