Skip to main content
Glama

Industrial MCP Server

by intecrel
route.ts7.44 kB
/** * OAuth 2.1 Token Endpoint * RFC 6749 - OAuth 2.0 Authorization Framework * Handles authorization code exchange for access tokens */ import { NextRequest, NextResponse } from 'next/server'; import { authenticateClient, validateRedirectUri } from '../../../../lib/oauth/clients'; import { validateScopes } from '../../../../lib/oauth/scopes'; import { validateToken, generateAccessToken, TokenClaims } from '../../../../lib/oauth/jwt'; import { verifyPkceChallenge, isValidCodeVerifier } from '../../../../lib/oauth/pkce'; export async function POST(request: NextRequest) { console.log('🔥 Token endpoint called'); try { // Parse form data or JSON body const contentType = request.headers.get('content-type') || ''; console.log(`🔥 Content-Type: ${contentType}`); let body: Record<string, string>; if (contentType.includes('application/x-www-form-urlencoded')) { const formData = await request.formData(); body = Object.fromEntries(formData.entries()) as Record<string, string>; } else if (contentType.includes('application/json')) { body = await request.json(); } else { return createErrorResponse('invalid_request', 'Unsupported content type'); } // Extract token request parameters const grant_type = body.grant_type; const code = body.code; const redirect_uri = body.redirect_uri; const client_id = body.client_id; const client_secret = body.client_secret; const code_verifier = body.code_verifier; console.log(`🔍 Token request: grant_type=${grant_type}, client_id=${client_id}, code=${code?.substring(0, 20)}..., redirect_uri=${redirect_uri}`); // Validate grant type if (grant_type !== 'authorization_code') { console.log(`❌ Invalid grant_type: ${grant_type}`); return createErrorResponse('unsupported_grant_type', 'Only authorization_code grant is supported'); } // Validate required parameters if (!code) { console.log('❌ Missing code parameter'); return createErrorResponse('invalid_request', 'Missing code parameter'); } if (!client_id) { console.log('❌ Missing client_id parameter'); return createErrorResponse('invalid_request', 'Missing client_id parameter'); } // Authenticate client - with fallback for Claude.ai dynamic registration let client; try { client = await authenticateClient(client_id, client_secret); } catch (error) { // If dynamic client not found, check if it's Claude.ai and use pre-registered client if (redirect_uri === 'https://claude.ai/api/mcp/auth_callback') { console.log(`🔄 Dynamic client ${client_id} not found in token endpoint, using pre-registered claude-web client`); try { client = await authenticateClient('claude-web', client_secret); } catch (fallbackError) { return createErrorResponse('invalid_client', fallbackError instanceof Error ? fallbackError.message : 'Client authentication failed'); } } else { return createErrorResponse('invalid_client', error instanceof Error ? error.message : 'Client authentication failed'); } } // Validate and decode authorization code let authClaims: TokenClaims; try { authClaims = await validateToken(code); console.log(`✅ Authorization code decoded: client_id=${authClaims.client_id}, scope=${authClaims.scope}`); } catch (error) { console.log(`❌ Authorization code validation failed: ${error instanceof Error ? error.message : 'Invalid authorization code'}`); return createErrorResponse('invalid_grant', error instanceof Error ? error.message : 'Invalid authorization code'); } // Verify authorization code properties if (authClaims.token_type !== 'authorization_code') { return createErrorResponse('invalid_grant', 'Invalid code type'); } // Verify the authorization code was issued to the requesting client or fallback client // For Claude.ai dynamic registration, allow either the original dynamic client_id or fallback client_id console.log(`🔍 Client ID validation: authClaims.client_id=${authClaims.client_id}, request client_id=${client_id}, authenticated client_id=${client.client_id}`); const isValidClientId = authClaims.client_id === client_id || authClaims.client_id === client.client_id || (redirect_uri === 'https://claude.ai/api/mcp/auth_callback' && client.client_id === 'claude-web'); if (!isValidClientId) { console.log(`❌ Client ID mismatch: code issued to ${authClaims.client_id}, request from ${client_id}, authenticated as ${client.client_id}`); return createErrorResponse('invalid_grant', 'Code was not issued to this client'); } // Validate redirect URI if present in the code if ('redirect_uri' in authClaims && redirect_uri !== authClaims.redirect_uri) { return createErrorResponse('invalid_grant', 'Redirect URI mismatch'); } // Validate PKCE if code challenge was used if ('code_challenge' in authClaims && authClaims.code_challenge) { if (!code_verifier) { return createErrorResponse('invalid_request', 'Missing code_verifier for PKCE'); } if (!isValidCodeVerifier(code_verifier)) { return createErrorResponse('invalid_request', 'Invalid code_verifier format'); } const codeChallengeMethod = authClaims.code_challenge_method || 'S256'; if (!verifyPkceChallenge(code_verifier, authClaims.code_challenge, codeChallengeMethod)) { return createErrorResponse('invalid_grant', 'PKCE verification failed'); } } // Parse and validate scopes from the authorization code const scopes = authClaims.scope.split(' ').filter(s => s.length > 0); const scopeValidation = validateScopes(authClaims.scope); if (!scopeValidation.valid) { return createErrorResponse('invalid_scope', scopeValidation.errors.join(', ')); } try { // Generate access token using the authenticated client's ID and user email from auth code const tokenResponse = await generateAccessToken( client.client_id, scopeValidation.scopes, authClaims.user_email ); console.log(`✅ Access token issued for client: ${client.client_name} with scopes: ${scopeValidation.scopes.join(' ')}`); return NextResponse.json(tokenResponse, { status: 200, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache' } }); } catch (error) { console.error('❌ Error generating access token:', error); return createErrorResponse('server_error', 'Failed to generate access token'); } } catch (error) { console.error('❌ Token endpoint error:', error); return createErrorResponse('server_error', 'Internal server error'); } } /** * Create JSON error response according to OAuth 2.1 spec */ function createErrorResponse(error: string, description: string): NextResponse { return NextResponse.json({ error, error_description: description }, { status: 400, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'Pragma': 'no-cache' } }); }

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