Skip to main content
Glama

Dodo Payments

Official
by dodopayments
app.ts3.5 kB
import { Hono } from 'hono'; import { layout, homeContent, parseApproveFormBody, renderAuthorizationApprovedContent, renderLoggedOutAuthorizeScreen, renderAuthorizationRejectedContent, } from './utils'; import type { OAuthHelpers } from '@cloudflare/workers-oauth-provider'; import { McpOptions } from 'dodopayments-mcp/server'; import { ServerConfig } from '.'; export type Bindings = Env & { OAUTH_PROVIDER: OAuthHelpers; }; export function makeOAuthConsent(config: ServerConfig, defaultOptions?: Partial<McpOptions>) { const app = new Hono<{ Bindings: Bindings; }>(); // Render a reasonable home page just to show the app is up app.get('/', async (c) => { const content = await homeContent(c.req.raw); return c.html(layout(content, 'Home', config)); }); // The /authorize page has a form that will POST to /approve app.get('/authorize', async (c) => { const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw); const content = await renderLoggedOutAuthorizeScreen(config, oauthReqInfo, defaultOptions); return c.html(layout(content, 'Authorization', config)); }); // This endpoint is responsible for validating any login information and // then completing the authorization request with the OAUTH_PROVIDER app.post('/approve', async (c) => { const { action, oauthReqInfo, clientProps, clientConfig } = await parseApproveFormBody( await c.req.parseBody(), config, ); if (action !== 'login_approve') { return c.html( layout( await renderAuthorizationRejectedContent(oauthReqInfo?.redirectUri || ''), 'Authorization Status', config, ), ); } if (!oauthReqInfo || !clientProps || !clientConfig) { return c.html('INVALID LOGIN', 401); } // We don't have a real user ID, just tokens, so we generate a random one // Make this some stable ID if you want to look up the user's grants later. const generatedUserId = crypto.randomUUID(); const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({ request: oauthReqInfo, userId: generatedUserId, metadata: {}, scope: oauthReqInfo.scope, props: { clientProps, clientConfig, }, }); return c.html( layout(await renderAuthorizationApprovedContent(redirectTo), 'Authorization Status', config), ); }); // Render the authorize screen for demoing the OAuth flow (it won't actually log in) app.get('/demo', async (c) => { const content = await renderLoggedOutAuthorizeScreen(config, {} as any, defaultOptions); return c.html(layout(content, 'Authorization', config)); }); // Add a resource server .well-known to point clients to the correct auth server const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, OPTIONS', 'Access-Control-Allow-Headers': '*', 'Access-Control-Max-Age': '86400', }; app.options('/.well-known/oauth-protected-resource', async (c) => { Object.entries(corsHeaders).forEach(([key, value]) => c.header(key, value)); return c.body(null, 204); }); app.get('/.well-known/oauth-protected-resource', async (c) => { Object.entries(corsHeaders).forEach(([key, value]) => c.header(key, value)); const baseURL = new URL('/', c.req.url).toString(); return c.json({ resource: baseURL, authorization_servers: [baseURL], }); }); return app; }

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/dodopayments/dodopayments-node'

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