Skip to main content
Glama

Authenticated Next.js MCP Server

route.ts3.87 kB
import { NextRequest, NextResponse } from 'next/server' import { oauthStorage, OAuthUtils } from '@/lib/oauth' export async function GET(request: NextRequest): Promise<NextResponse> { const { searchParams } = new URL(request.url) const response_type = searchParams.get('response_type') const client_id = searchParams.get('client_id') const redirect_uri = searchParams.get('redirect_uri') const scope = searchParams.get('scope') || '' const state = searchParams.get('state') const code_challenge = searchParams.get('code_challenge') const code_challenge_method = searchParams.get('code_challenge_method') as | 'S256' | 'plain' | null // Validate required parameters if (!response_type || !client_id || !redirect_uri) { return NextResponse.json( { error: 'invalid_request', error_description: 'Missing required parameters', }, { status: 400 } ) } if (response_type !== 'code') { return NextResponse.json( { error: 'unsupported_response_type', error_description: 'Only authorization code flow is supported', }, { status: 400 } ) } // Validate client const client = oauthStorage.getClient(client_id) if (!client) { return NextResponse.json( { error: 'invalid_client', error_description: 'Unknown client' }, { status: 400 } ) } // Validate redirect URI if (!client.redirect_uris.includes(redirect_uri)) { return NextResponse.json( { error: 'invalid_request', error_description: 'Invalid redirect URI' }, { status: 400 } ) } // Validate PKCE (recommended for public clients) if (code_challenge && !code_challenge_method) { return NextResponse.redirect( `${redirect_uri}?error=invalid_request&error_description=code_challenge_method is required when code_challenge is present&state=${state}` ) } try { // Generate authorization code const code = OAuthUtils.generateAuthorizationCode() const authCode = { code, client_id, redirect_uri, scope, code_challenge: code_challenge || undefined, code_challenge_method: code_challenge_method || undefined, expires_at: Date.now() + 10 * 60 * 1000, // 10 minutes user_id: 'default-user', // In real app, get from session } oauthStorage.storeAuthCode(authCode) // Redirect with authorization code const redirectUrl = new URL(redirect_uri) redirectUrl.searchParams.set('code', code) if (state) redirectUrl.searchParams.set('state', state) return NextResponse.redirect(redirectUrl.toString()) } catch { const redirectUrl = new URL(redirect_uri) redirectUrl.searchParams.set('error', 'server_error') redirectUrl.searchParams.set('error_description', 'Internal server error') if (state) redirectUrl.searchParams.set('state', state) return NextResponse.redirect(redirectUrl.toString()) } } export async function POST(request: NextRequest): Promise<NextResponse> { // Handle consent form submission (for interactive flows) const formData = await request.formData() const action = formData.get('action') if (action === 'allow') { // User granted permission, redirect to GET handler const url = new URL(request.url) return GET( new NextRequest( url.toString().replace('/oauth/authorize', '/oauth/authorize') ) ) } else { // User denied permission const redirect_uri = formData.get('redirect_uri') as string const state = formData.get('state') as string const redirectUrl = new URL(redirect_uri) redirectUrl.searchParams.set('error', 'access_denied') redirectUrl.searchParams.set('error_description', 'User denied the request') if (state) redirectUrl.searchParams.set('state', state) return NextResponse.redirect(redirectUrl.toString()) } }

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/vedaterenoglu/ve-nextjs-mcp-server'

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