okta-mcp-server
Version:
Model Context Protocol (MCP) server for Okta API operations with support for bulk operations and caching
82 lines • 3.2 kB
JavaScript
/**
* Simple MCP OAuth middleware for validating bearer tokens
* This allows the MCP server to be protected by OAuth if desired
*/
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
import { protocolSafeLogger as logger } from '../../utils/protocol-safe-logger.js';
import * as jwt from 'jsonwebtoken';
export class SimpleAuthMiddleware {
config;
constructor(config) {
this.config = config;
}
/**
* Extract bearer token from MCP request metadata
*/
extractBearerToken(request) {
// Check Authorization header in metadata
const metadata = request.metadata || {};
const authHeader = metadata.authorization || metadata.Authorization;
if (authHeader && typeof authHeader === 'string') {
const match = authHeader.match(/^Bearer\s+(.+)$/i);
if (match) {
return match[1];
}
}
return undefined;
}
/**
* Validate bearer token and authenticate request
*/
async authenticate(request) {
// If auth is not required, skip
if (!this.config.required) {
return;
}
// Special case: allow oauth/metadata endpoint without auth
if (request.method === 'oauth/metadata') {
return;
}
// Extract and validate token
const token = this.extractBearerToken(request);
if (!token) {
throw new McpError(ErrorCode.InvalidRequest, 'Authentication required. Please provide a valid Bearer token.');
}
try {
// Basic JWT validation - just check structure and expiry
// In production, you'd verify signature with the issuer's public key
const decoded = jwt.decode(token, { complete: true });
if (!decoded || typeof decoded.payload !== 'object') {
throw new Error('Invalid token format');
}
const payload = decoded.payload;
// Check expiry
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
throw new Error('Token expired');
}
// Check audience if configured
if (this.config.audience) {
const aud = Array.isArray(payload.aud) ? payload.aud : [payload.aud];
if (!aud.includes(this.config.audience)) {
throw new Error('Invalid audience');
}
}
// Check issuer if configured
if (this.config.issuer && payload.iss !== this.config.issuer) {
throw new Error('Invalid issuer');
}
logger.debug('Authentication successful', {
method: request.method,
sub: payload.sub,
});
}
catch (error) {
logger.error('Authentication failed', {
method: request.method,
error: error instanceof Error ? error.message : 'Unknown error',
});
throw new McpError(ErrorCode.InvalidRequest, 'Invalid or expired token. Please authenticate again.');
}
}
}
//# sourceMappingURL=oauth-middleware.js.map