Skip to main content
FrontMCP includes a built-in OAuth 2.1 authorization server for self-contained authentication scenarios.
The built-in login page accepts any email format without validation. Replace with a real identity provider for production use.

Basic Configuration

@FrontMcp({
  info: { name: 'MyServer', version: '1.0.0' },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
  },
})
export class Server {}

Configuration Options

OptionTypeDefaultDescription
consentConsentConfig{ enabled: false }Consent UI configuration
sessionMode'stateful' | 'stateless''stateful'Session management strategy
tokenStorageobject{ type: 'memory' }Token storage backend
allowDefaultPublicbooleanfalseAllow unauthenticated access
anonymousScopesstring[]['anonymous']Scopes for anonymous sessions
incrementalAuthIncrementalAuthConfig{ enabled: true }Progressive authorization config

OAuth Endpoints

Local mode exposes standard OAuth 2.1 endpoints:
EndpointMethodDescription
/oauth/authorizeGETStart authorization flow
/oauth/tokenPOSTExchange code for tokens
/oauth/registerPOSTDynamic Client Registration
/oauth/userinfoGETGet user profile
/.well-known/oauth-authorization-serverGETServer metadata
/.well-known/jwks.jsonGETPublic signing keys

Authorization Request

GET /oauth/authorize
  ?response_type=code
  &client_id=your-client-id
  &redirect_uri=http://localhost:3000/callback
  &scope=openid profile
  &state=random-state
  &code_challenge=base64url(sha256(verifier))
  &code_challenge_method=S256

Token Exchange

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=authorization-code
&redirect_uri=http://localhost:3000/callback
&client_id=your-client-id
&code_verifier=original-verifier

Key Management

Auto-Generated Keys

By default, FrontMCP generates RS256 keys at startup:
auth: {
  mode: 'orchestrated',
  type: 'local',
  // Keys auto-generated
}
Auto-generated keys are lost on restart. Existing tokens become invalid.

Persistent Keys

Provide keys for stable token validation:
auth: {
  mode: 'orchestrated',
  type: 'local',
  local: {
    signKey: {
      kty: 'RSA',
      kid: 'my-key-id',
      alg: 'RS256',
      n: '...',  // RSA modulus
      e: 'AQAB',
      d: '...',  // Private exponent
      // ... other JWK parameters
    },
  },
}

ES256 Keys

Use ES256 for smaller tokens:
auth: {
  mode: 'orchestrated',
  type: 'local',
  local: {
    signKey: {
      kty: 'EC',
      crv: 'P-256',
      x: '...',
      y: '...',
      d: '...',
    },
  },
}

Dynamic Client Registration

DCR allows clients to register programmatically. DCR is enabled by default in development.

Registration Request

POST /oauth/register
Content-Type: application/json

{
  "redirect_uris": ["http://localhost:3000/callback"],
  "client_name": "My Application",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

Registration Response

{
  "client_id": "generated-uuid",
  "client_id_issued_at": 1234567890,
  "redirect_uris": ["http://localhost:3000/callback"],
  "client_name": "My Application",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}
DCR is intended for development only. In production, pre-register clients. DCR only allows localhost redirect URIs by default.

Per-App Configuration

Configure local auth per app with splitByApp: true:
@App({
  name: 'Billing',
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
  },
})
export class BillingApp {}

@App({
  name: 'Analytics',
  auth: {
    mode: 'orchestrated',
    type: 'local',
  },
})
export class AnalyticsApp {}

@FrontMcp({
  info: { name: 'Suite', version: '1.0.0' },
  apps: [BillingApp, AnalyticsApp],
  splitByApp: true,
})
export class Server {}

Token Storage

In-Memory (Development)

auth: {
  mode: 'orchestrated',
  type: 'local',
  tokenStorage: { type: 'memory' },
}

Redis (Production)

auth: {
  mode: 'orchestrated',
  type: 'local',
  tokenStorage: {
    type: 'redis',
    config: {
      host: process.env.REDIS_HOST!,
      port: parseInt(process.env.REDIS_PORT || '6379'),
      password: process.env.REDIS_PASSWORD,
      keyPrefix: 'myapp:auth:',
    },
  },
}

Enable and customize the consent UI:
auth: {
  mode: 'orchestrated',
  type: 'local',
  consent: {
    enabled: true,
    groupByApp: true,           // Group tools by app
    showDescriptions: true,     // Show tool descriptions
    allowSelectAll: true,       // Allow selecting all tools
    requireSelection: true,     // Require at least one tool
    rememberConsent: true,      // Remember for future sessions
    excludedTools: ['health'],  // Always available tools
    defaultSelectedTools: [],   // Pre-selected tools
    customMessage: 'Select the tools you want to grant access to.',
  },
}

Complete Example

import { FrontMcp } from '@frontmcp/sdk';

@FrontMcp({
  info: { name: 'MyServer', version: '1.0.0' },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    consent: { enabled: true },
    sessionMode: 'stateful',
    tokenStorage: {
      type: 'redis',
      config: {
        host: process.env.REDIS_HOST!,
        port: parseInt(process.env.REDIS_PORT || '6379'),
        password: process.env.REDIS_PASSWORD,
      },
    },
    refresh: {
      enabled: true,
      skewSeconds: 60,
    },
    local: {
      signKey: JSON.parse(process.env.JWT_SIGNING_KEY!),
    },
    incrementalAuth: {
      enabled: true,
      allowSkip: true,
      skippedAppBehavior: 'require-auth',
    },
  },
})
export class Server {}

Troubleshooting

Auto-generated keys are lost on restart. Either:
  • Provide persistent keys via local.signKey
  • Use Redis for token storage
  • Accept that users must re-authenticate
Ensure you’re using S256 challenge method and the code_verifier matches the original code_challenge.
When running multiple server instances, use Redis for token storage to share session state.

Next Steps