Skip to main content
Glama

Industrial MCP Server

by intecrel
route.ts5.62 kB
/** * OAuth Token Revocation Endpoint (RFC 7009) * Allows users and clients to revoke access tokens and refresh tokens */ import { NextRequest, NextResponse } from 'next/server'; import { getServerSession } from 'next-auth'; import { authOptions } from '../../../../lib/auth/config'; import { validateToken } from '../../../../lib/oauth/jwt'; import { withRateLimit, RATE_LIMITS } from '../../../../lib/security/rate-limiter'; import { createAuditLogger, AuditEventType } from '../../../../lib/security/audit-logger'; import { addToBlacklist, isTokenRevoked } from '../../../../lib/oauth/token-blacklist'; interface TokenRevocationRequest { token: string; token_type_hint?: 'access_token' | 'refresh_token'; } async function revokeHandler(request: NextRequest): Promise<Response> { try { const session = await getServerSession(authOptions); // Allow both authenticated users and clients to revoke tokens const contentType = request.headers.get('content-type'); let body: TokenRevocationRequest; // Parse request body (support both JSON and form-encoded) if (contentType?.includes('application/json')) { body = await request.json(); } else { const formData = await request.formData(); body = { token: formData.get('token')?.toString() || '', token_type_hint: formData.get('token_type_hint')?.toString() as any }; } const { token, token_type_hint } = body; if (!token) { return NextResponse.json({ error: 'invalid_request', error_description: 'Missing token parameter' }, { status: 400 }); } const logger = createAuditLogger(request, session?.user); try { // Validate the token to get its claims const tokenClaims = await validateToken(token); // Check if user is authorized to revoke this token if (session?.user) { // User is revoking their own token - verify ownership if (tokenClaims.user_email && tokenClaims.user_email !== session.user.email) { logger.logSecurityEvent( AuditEventType.SECURITY_UNAUTHORIZED_ACCESS, 'Attempted to revoke another user\'s token', 'failure', { token_user_email: tokenClaims.user_email, requesting_user_email: session.user.email, token_preview: token.substring(0, 16) + '...' }, 'high' ); return NextResponse.json({ error: 'unauthorized', error_description: 'Cannot revoke token belonging to another user' }, { status: 403 }); } } // Add token to blacklist await addToBlacklist(token); // Log successful revocation logger.logOAuthEvent( AuditEventType.OAUTH_TOKEN_REVOKED, `Token revoked successfully`, tokenClaims.client_id, 'success', { token_type: tokenClaims.token_type, token_type_hint, token_preview: token.substring(0, 16) + '...', revoked_by: session?.user?.email || 'client', scopes: tokenClaims.scope, expires_at: new Date(tokenClaims.exp * 1000).toISOString() }, 'medium' ); console.log(`✅ Token revoked successfully for client: ${tokenClaims.client_id}`); // Return 200 OK (RFC 7009 specifies this even for invalid tokens) return NextResponse.json({}, { status: 200 }); } catch (tokenError) { // Token is invalid or expired - still return 200 OK per RFC 7009 logger.log( AuditEventType.OAUTH_TOKEN_REVOKED, 'Token revocation attempted (invalid/expired token)', 'success', { token_preview: token.substring(0, 16) + '...', error: tokenError instanceof Error ? tokenError.message : 'Invalid token', revoked_by: session?.user?.email || 'client' }, 'low' ); console.log(`⚠️ Token revocation attempted for invalid/expired token: ${token.substring(0, 8)}...`); // Still add to blacklist in case it's a malformed but dangerous token await addToBlacklist(token); return NextResponse.json({}, { status: 200 }); } } catch (error) { console.error('❌ Token revocation error:', error); return NextResponse.json({ error: 'server_error', error_description: 'Failed to process token revocation' }, { status: 500 }); } } // Token revocation checking is now handled by lib/oauth/token-blacklist.ts /** * Get revocation status endpoint (for debugging/admin) */ async function getRevocationStatus(request: NextRequest): Promise<Response> { const session = await getServerSession(authOptions); if (!session?.user) { return NextResponse.json({ error: 'unauthorized', error_description: 'Authentication required' }, { status: 401 }); } const url = new URL(request.url); const token = url.searchParams.get('token'); if (!token) { return NextResponse.json({ error: 'invalid_request', error_description: 'Missing token parameter' }, { status: 400 }); } const revoked = await isTokenRevoked(token); return NextResponse.json({ token_preview: token.substring(0, 16) + '...', revoked, checked_at: new Date().toISOString() }); } // Export rate-limited handlers export const POST = withRateLimit(RATE_LIMITS.OAUTH_CONSENT, revokeHandler); export const GET = withRateLimit(RATE_LIMITS.GENERAL, getRevocationStatus);

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/intecrel/industrial-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server