import type {
AuthRequest,
OAuthHelpers
} from "@cloudflare/workers-oauth-provider";
import { Hono } from "hono";
interface Env {
OAUTH_PROVIDER: OAuthHelpers;
}
const app = new Hono<{ Bindings: Env }>();
/**
* GET /authorize - OAuth authorization endpoint
*
* This endpoint is called when an MCP client wants to authorize.
* In a full implementation, this would:
* 1. Parse the OAuth request
* 2. Check if the client is already approved (via cookie)
* 3. Show an approval dialog or redirect to external OAuth provider
*/
app.get("/authorize", async (c) => {
const oauthReqInfo: AuthRequest = await c.env.OAUTH_PROVIDER.parseAuthRequest(
c.req.raw
);
const clientInfo = await c.env.OAUTH_PROVIDER.lookupClient(
oauthReqInfo.clientId
);
if (!clientInfo) {
return c.text("Invalid client_id", 400);
}
// For this demo, we'll show a simple HTML approval page
// In a real implementation, you might:
// - Check cookies to see if this client was previously approved
// - Redirect to an external OAuth provider (GitHub, Google, etc.)
// - Show a custom approval UI
const approvalPage = `
Authorize ${clientInfo.clientName || "MCP Client"}
Authorization Request
${clientInfo.clientName || "An MCP Client"} is requesting access to the MCP server.
Client ID: ${clientInfo.clientId}
${clientInfo.clientUri ? `
Website: ${clientInfo.clientUri}
` : ""}
Requested Scopes: ${oauthReqInfo.scope.join(", ") || "none"}
If you approve, this client will be able to:
- Access MCP tools on your behalf
- Receive your authenticated user information
`;
return c.html(approvalPage);
});
/**
* POST /authorize - Handle authorization approval
*
* This endpoint is called when the user approves the authorization.
* It completes the OAuth flow by creating a grant and redirecting back to the client.
*/
app.post("/authorize", async (c) => {
const formData = await c.req.formData();
const state = formData.get("state");
if (!state || typeof state !== "string") {
return c.text("Missing state parameter", 400);
}
let oauthReqInfo: AuthRequest;
try {
oauthReqInfo = JSON.parse(atob(state));
} catch {
return c.text("Invalid state parameter", 400);
}
// For this demo, we'll use a static user ID
// In a real implementation, you would:
// 1. Have already authenticated the user (via external OAuth, session, etc.)
// 2. Use their actual user ID and profile information
const userId = "demo-user";
const userProfile = {
userId: "demo-user",
username: "Demo User",
email: "demo@example.com"
};
// Complete the authorization by creating a grant
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
request: oauthReqInfo,
userId: userId,
metadata: {
label: "MCP Server Access",
clientName:
(await c.env.OAUTH_PROVIDER.lookupClient(oauthReqInfo.clientId))
?.clientName || "Unknown Client"
},
scope: oauthReqInfo.scope,
props: userProfile
});
// Redirect back to the client with the authorization code
return c.redirect(redirectTo, 302);
});
/**
* GET / - Home page
*
* Shows information about the OAuth server
*/
app.get("/", (c) => {
return c.html(`
MCP OAuth Server
MCP OAuth Server
This is an authenticated MCP server that uses OAuth 2.1 for authorization.
Available Endpoints
/mcp - MCP server endpoint (requires Bearer token)
/authorize - OAuth authorization endpoint
/token - OAuth token endpoint
/register - Client registration endpoint
/.well-known/oauth-authorization-server - OAuth metadata
Getting Started
To connect to this MCP server:
- Register your MCP client via the
/register endpoint
- Initiate the OAuth flow by redirecting to
/authorize
- Exchange the authorization code for an access token at
/token
- Use the access token to access the
/mcp endpoint
`);
});
export { app as AuthHandler };