Imagine you've built an AI-driven sales analytics tool used by enterprise SaaS businesses. It aggregates sales data, integrates with CRMs, and helps companies forecast revenue accurately.
In today’s AI-centric ecosystem, you would need to have the capability to use conversational prompts to surface the right insights. How do you securely handle authentication and manage API requests at scale without exposing sensitive customer data?
MCP servers act as a secure layer that enables your AI application to interact safely and efficiently with external systems. With an MCP server in place, your sales analytics tool can securely handle auth, manage access to sensitive data, and control interactions between your app and client systems, all without exposing direct access credentials or data endpoints.
As your MCP server moves from prototype to production, OAuth 2.1 authentication becomes critical. Here’s a step-by-step guide on securely implementing OAuth 2.1, with code examples.
OAuth implementation overview
Implementing OAuth for MCP involves four core steps:
- Register your MCP server and define OAuth scopes
- Expose OAuth protected resource metadata
- Validate JWT tokens
- Implement scope-based authorization
Let's dive right in.
Step 1: Register your MCP server
First, register your MCP server in your Scalekit dashboard:
- Server name: e.g., "Sales Analytics API"
-
Resource identifier: Unique URL (e.g.,
https://api.sales-analytics.com
) - Dynamic client registration: Enabled for automatic onboarding
- Token lifetime: Access tokens (5 min–1 hr), refresh tokens (up to 24 hrs)
Define your OAuth scopes
Scopes should clearly represent permissions. Examples:
-
mcp:tools:sales-data:read
- Read sales data -
mcp:tools:crm:write
- Update CRM records -
mcp:resources:customer-insights:read
- Access customer insights
Step 2: OAuth protected resource metadata
Your MCP server exposes metadata for clients to auto-discover OAuth endpoints:
app.get('/.well-known/oauth-protected-resource', (req, res) => {
res.json({
resource: 'https://api.sales-analytics.com',
authorization_servers: ['https://your-org.scalekit.com'],
bearer_methods_supported: ['header'],
resource_documentation: 'https://api.sales-analytics.com/docs',
scopes_supported: [
'mcp:tools:sales-data',
'mcp:tools:crm:read',
'mcp:tools:crm:write',
'mcp:tools:notifications:send',
'mcp:resources:*'
]
});
});
Step 3: Validate JWT tokens
Each request to your MCP endpoints must validate a JWT token:
import { jwtVerify, createRemoteJWKSet } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://your-org.scalekit.com/.well-known/jwks')
);
const validateToken = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Bearer token required' });
}
try {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://your-org.scalekit.com',
audience: 'https://api.sales-analytics.com'
});
req.auth = {
userId: payload.sub,
scopes: payload.scope?.split(' ') || [],
clientId: payload.client_id,
expiresAt: payload.exp
};
next();
} catch (error) {
return res.status(401).json({ error: 'Invalid or expired token' });
}
};
app.use('/mcp', validateToken);
Step 4: Scope-based authorization
Scopes provide granular permissions:
const requireScope = (requiredScope) => (req, res, next) => {
const userScopes = req.auth.scopes;
const hasScope = userScopes.some(scope =>
scope === requiredScope || scope.endsWith(':*') && requiredScope.startsWith(scope.slice(0, -1))
);
if (!hasScope) {
return res.status(403).json({
error: 'insufficient_scope',
required_scope: requiredScope
});
}
next();
};
app.post('/mcp/tools/sales-data',
requireScope('mcp:tools:sales-data:read'),
handleSalesDataRequest
);
Testing your implementation
Thoroughly test OAuth integration:
const testMCPAuth = async () => {
const tokenResponse = await fetch('https://your-org.scalekit.com/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: 'test-client-id',
client_secret: 'test-client-secret',
scope: 'mcp:tools:sales-data'
})
});
const { access_token } = await tokenResponse.json();
const response = await fetch('https://api.sales-analytics.com/mcp/tools/sales-data', {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
method: 'sales/get_forecast',
params: { region: 'North America' }
})
});
console.log(await response.json());
};
For further details, explore our comprehensive guide: Implement OAuth authentication for MCP servers using Scalekit.
Top comments (0)