Skip to main content
Before deploying FrontMCP authentication to production, review this comprehensive checklist covering security, configuration, and operational requirements.

Pre-Deployment Checklist

1

Configure Persistent Token Storage

Replace in-memory storage with Redis or another persistent store.
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,
      tls: true,
    },
  },
}
2

Provide Persistent Signing Keys

Auto-generated keys are lost on restart. Provide stable keys.
auth: {
  local: {
    signKey: JSON.parse(process.env.JWT_SIGNING_KEY),
  },
}
3

Configure Token Lifetimes

Set appropriate token expiration based on security requirements.
auth: {
  refresh: {
    accessTokenTtl: 900,      // 15 minutes
    refreshTokenTtl: 604800,  // 7 days
    rotateRefreshToken: true,
  },
}
4

Enable HTTPS

All production traffic must use TLS. Configure your reverse proxy or load balancer.
5

Set Expected Audience

For transparent mode, always validate the audience claim.
auth: {
  mode: 'transparent',
  expectedAudience: 'https://api.yourservice.com',
}

Security Requirements

Required

HTTPS only - Never expose auth endpoints over HTTP in production
Persistent token storage - Redis with TLS for multi-instance deployments
Stable signing keys - Provide keys via environment or secrets manager
PKCE enforcement - OAuth 2.1 requires PKCE (S256 only)
Refresh token rotation - Always rotate refresh tokens on use
Audience validation - Validate aud claim in transparent mode
Rate limiting - Protect token endpoints from brute force
Monitoring - Alert on auth failures, unusual patterns
Key rotation - Rotate signing keys periodically (30-90 days)
Scope minimization - Request only necessary scopes
Consent UI - Let users see what they’re authorizing

Configuration Reference

Minimum Production Configuration

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

@FrontMcp({
  info: { name: 'ProductionServer', version: '1.0.0' },
  auth: {
    mode: 'orchestrated',
    type: 'local',
    sessionMode: 'stateful',

    // Persistent storage
    tokenStorage: {
      type: 'redis',
      config: {
        host: process.env.REDIS_HOST!,
        port: parseInt(process.env.REDIS_PORT || '6379'),
        password: process.env.REDIS_PASSWORD,
        tls: true,
        keyPrefix: 'auth:',
      },
    },

    // Stable keys
    local: {
      signKey: JSON.parse(process.env.JWT_SIGNING_KEY!),
    },

    // Token refresh settings
    refresh: {
      enabled: true,
      skewSeconds: 60,
    },

    // Security
    consent: { enabled: true },
  },
})
export class Server {}

Environment Variables

# Required for Redis
REDIS_HOST=redis.example.com
REDIS_PORT=6379
REDIS_PASSWORD=your-redis-password

# Required for token signing
JWT_SIGNING_KEY='{"kty":"RSA","kid":"prod-key-1",...}'

# For remote/transparent mode
IDP_PROVIDER_URL=https://auth.example.com
IDP_CLIENT_ID=your-client-id
IDP_CLIENT_SECRET=your-client-secret
IDP_EXPECTED_AUDIENCE=https://api.yourservice.com

Token Lifetime Guidelines

EnvironmentAccess TokenRefresh TokenRationale
Development1 hour7 daysDeveloper convenience
Staging30 min7 daysBalance testing/security
Production15 min7-30 daysSecurity with usability
High Security5 min1 dayMinimize exposure window
Shorter access tokens improve security but increase refresh frequency. Monitor your refresh token endpoint load.

Key Management

Generating Production Keys

# Generate RS256 key pair
openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -pubout -out public.pem

# Convert to JWK format (use a tool like step-cli)
step crypto jwk create --kty RSA --use sig --alg RS256 \
  public.jwk private.jwk

Key Rotation Strategy

Configuration for Key Rotation

auth: {
  local: {
    signKey: currentKey,
    keyRotationDays: 30,
    maxKeys: 3, // Keep old keys for validation during rotation
  },
}

Redis Configuration

Secure Redis Connection

tokenStorage: {
  type: 'redis',
  config: {
    host: 'redis.example.com',
    port: 6379,
    password: process.env.REDIS_PASSWORD,
    tls: true,
    keyPrefix: 'frontmcp:auth:',
  },
}

Redis Data Stored

Key PatternDataTTL
{prefix}pending:{id}Pending authorization10 min
{prefix}code:{code}Authorization code60 sec
{prefix}refresh:{token}Refresh token7-30 days
{prefix}session:{id}Session dataConfigurable
{prefix}vault:{session}Token vaultSession TTL

Monitoring & Alerting

Key Metrics to Monitor

MetricAlert ThresholdDescription
Auth failures/min> 100Possible brute force
Token refresh rate> 10x normalPossible token theft
JWKS fetch errorsAnyIdP connectivity issue
Redis connection errorsAnyStorage unavailable
Token issuance latency> 500msPerformance degradation

Logging Configuration

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

@FrontMcp({
  info: { name: 'Server', version: '1.0.0' },
  logging: {
    level: LogLevel.INFO,
    // Don't log tokens or secrets
    redact: ['access_token', 'refresh_token', 'client_secret'],
  },
})
export class Server {}

Common Production Issues

Cause: Auto-generated keys changed after restart.Solution: Provide persistent signing keys via environment variable:
local: {
  signKey: JSON.parse(process.env.JWT_SIGNING_KEY!),
}
Cause: Using in-memory storage with multiple instances.Solution: Configure shared Redis storage:
tokenStorage: {
  type: 'redis',
  config: {
    host: process.env.REDIS_HOST!,
    port: parseInt(process.env.REDIS_PORT || '6379'),
    password: process.env.REDIS_PASSWORD,
  },
}
Cause: Network issues or IdP rate limiting.Solution:
  • Increase JWKS cache TTL
  • Consider inline JWKS for critical IdPs
  • Add retry logic with exponential backoff
Cause: Redis connection issues or key generation overhead.Solution:
  • Use Redis connection pooling
  • Pre-generate signing keys (don’t generate on-demand)
  • Monitor Redis latency

Security Hardening

Disable Insecure Options

auth: {
  mode: 'orchestrated',
  type: 'local',

  // Production settings
  allowDefaultPublic: false,   // No anonymous access
  dcrEnabled: false,           // Disable DCR in production

  // Use orchestrated mode login page or remote IdP
  // Never use the demo login page in production
}

HTTP Security Headers

Configure your reverse proxy to add security headers:
# Example nginx configuration
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Content-Security-Policy "default-src 'self'" always;

Rate Limiting

# Rate limit token endpoint
location /oauth/token {
    limit_req zone=token_limit burst=10 nodelay;
    proxy_pass http://backend;
}

Deployment Patterns

// Simple deployment - in-memory works but not recommended
auth: {
  tokenStorage: { type: 'memory' }, // Data lost on restart
}
In-memory storage loses all sessions on restart. Users must re-authenticate.

Quick Reference

OAuth 2.1 Compliance Checklist

PKCE required for all clients (S256 only)
Refresh token rotation enabled
No implicit grant flow
Authorization codes single-use
Redirect URI exact match
State parameter required

Pre-Production Verification

# Test token endpoint
curl -X POST https://your-server/oauth/token \
  -d "grant_type=authorization_code" \
  -d "code=test-code" \
  -d "client_id=your-client" \
  -d "code_verifier=verifier"

# Verify JWKS endpoint
curl https://your-server/.well-known/jwks.json

# Check server metadata
curl https://your-server/.well-known/oauth-authorization-server

Next Steps