Skip to main content
Glama

mcp-server-cloudflare

Official
by cloudflare
cloudflare-auth.ts5.01 kB
import { z } from 'zod' import { McpError } from './mcp-error' import type { AuthRequest } from '@cloudflare/workers-oauth-provider' // Constants const PKCE_CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~' const RECOMMENDED_CODE_VERIFIER_LENGTH = 96 function base64urlEncode(value: string): string { let base64 = btoa(value) base64 = base64.replace(/\+/g, '-') base64 = base64.replace(/\//g, '_') base64 = base64.replace(/=/g, '') return base64 } interface PKCECodes { codeChallenge: string codeVerifier: string } async function generatePKCECodes(): Promise<PKCECodes> { const output = new Uint32Array(RECOMMENDED_CODE_VERIFIER_LENGTH) crypto.getRandomValues(output) const codeVerifier = base64urlEncode( Array.from(output) .map((num: number) => PKCE_CHARSET[num % PKCE_CHARSET.length]) .join('') ) const buffer = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier)) const hash = new Uint8Array(buffer) let binary = '' const hashLength = hash.byteLength for (let i = 0; i < hashLength; i++) { binary += String.fromCharCode(hash[i]) } const codeChallenge = base64urlEncode(binary) //btoa(binary); return { codeChallenge, codeVerifier } } function generateAuthUrl({ client_id, redirect_uri, state, code_challenge, scopes, }: { client_id: string redirect_uri: string code_challenge: string state: string scopes: Record<string, string> }) { const params = new URLSearchParams({ response_type: 'code', client_id, redirect_uri, state, code_challenge, code_challenge_method: 'S256', scope: Object.keys(scopes).join(' '), }) const upstream = new URL(`https://dash.cloudflare.com/oauth2/auth?${params.toString()}`) return upstream.href } /** * Constructs an authorization URL for Cloudflare. * * @param {Object} options * @param {string} options.client_id - The client ID of the application. * @param {string} options.redirect_uri - The redirect URI of the application. * @param {string} [options.state] - The state parameter. * * @returns {string} The authorization URL. */ export async function getAuthorizationURL({ client_id, redirect_uri, state, scopes, }: { client_id: string redirect_uri: string state: AuthRequest scopes: Record<string, string> }): Promise<{ authUrl: string; codeVerifier: string }> { const { codeChallenge, codeVerifier } = await generatePKCECodes() return { authUrl: generateAuthUrl({ client_id, redirect_uri, state: btoa(JSON.stringify({ ...state, codeVerifier })), code_challenge: codeChallenge, scopes, }), codeVerifier: codeVerifier, } } type AuthorizationToken = z.infer<typeof AuthorizationToken> const AuthorizationToken = z.object({ access_token: z.string(), expires_in: z.number(), refresh_token: z.string(), scope: z.string(), token_type: z.string(), }) /** * Fetches an authorization token from Cloudflare. * * @param {Object} options * @param {string} options.client_id - The client ID of the application. * @param {string} options.client_secret - The client secret of the application. * @param {string} options.code - The authorization code. * @param {string} options.redirect_uri - The redirect URI of the application. * * @returns {Promise<[string, null] | [null, Response]>} A promise that resolves to an array containing the access token or an error response. */ export async function getAuthToken({ client_id, client_secret, redirect_uri, code_verifier, code, }: { client_id: string client_secret: string redirect_uri: string code_verifier: string code: string }): Promise<AuthorizationToken> { if (!code) { throw new McpError('Missing code', 400) } const params = new URLSearchParams({ grant_type: 'authorization_code', client_id, redirect_uri, code, code_verifier, }).toString() const resp = await fetch('https://dash.cloudflare.com/oauth2/token', { method: 'POST', headers: { Authorization: `Basic ${btoa(`${client_id}:${client_secret}`)}`, 'Content-Type': 'application/x-www-form-urlencoded', }, body: params, }) if (!resp.ok) { console.log(await resp.text()) throw new McpError('Failed to get OAuth token', 500, { reportToSentry: true }) } return AuthorizationToken.parse(await resp.json()) } export async function refreshAuthToken({ client_id, client_secret, refresh_token, }: { client_id: string client_secret: string refresh_token: string }): Promise<AuthorizationToken> { const params = new URLSearchParams({ grant_type: 'refresh_token', client_id, refresh_token, }) const resp = await fetch('https://dash.cloudflare.com/oauth2/token', { method: 'POST', body: params.toString(), headers: { Authorization: `Basic ${btoa(`${client_id}:${client_secret}`)}`, 'Content-Type': 'application/x-www-form-urlencoded', }, }) if (!resp.ok) { console.log(await resp.text()) throw new McpError('Failed to get OAuth token', 500, { reportToSentry: true }) } return AuthorizationToken.parse(await resp.json()) }

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/cloudflare/mcp-server-cloudflare'

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