Skip to content

Commit a925043

Browse files
committed
Add auth-server for handling callbacks
1 parent 7b2f086 commit a925043

File tree

17 files changed

+5912
-65
lines changed

17 files changed

+5912
-65
lines changed

apps/auth-server/.eslintrc.cjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/** @type {import("eslint").Linter.Config} */
2+
module.exports = {
3+
root: true,
4+
extends: ['@repo/eslint-config/default.cjs'],
5+
}

apps/auth-server/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Auth Server
2+
3+
This server is responsible for handling callbacks from Cloudflare oauth flow, and then redirecting them to a whitelisted server's callback URL e.g workers/observability/oauth/callback.
4+
5+
This simplifies the oauth authentication implementation for MCP servers within this monorepo, as each MCP server simply needs to be added to the whitelisted callback url list and then each server can handle the forwarded callback from auth-server.
6+
7+
Note: In development, the auth-server is not needed, as each server (e.g workers/observability) will simply handle the callback on the <http://localhost:8976/oauth/callback> endpoint.

apps/auth-server/package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "auth-server",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"check:lint": "run-eslint-workers",
7+
"check:types": "run-tsc",
8+
"deploy": "wrangler deploy",
9+
"dev": "wrangler dev",
10+
"start": "wrangler dev",
11+
"cf-typegen": "wrangler types",
12+
"test": "vitest run"
13+
},
14+
"dependencies": {
15+
"@hono/zod-validator": "0.4.3",
16+
"@repo/mcp-common": "workspace:*",
17+
"hono": "4.7.6",
18+
"zod": "3.24.2"
19+
},
20+
"devDependencies": {
21+
"@cloudflare/vitest-pool-workers": "0.8.14",
22+
"@cloudflare/workers-types": "4.20250410.0",
23+
"prettier": "3.5.3",
24+
"typescript": "5.5.4",
25+
"vitest": "3.0.9",
26+
"wrangler": "4.9.1"
27+
}
28+
}

apps/auth-server/src/index.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import { zValidator } from '@hono/zod-validator'
2+
import { Hono } from 'hono'
3+
4+
import {
5+
AuthQuery,
6+
AuthRequestSchemaWithExtraParams,
7+
ValidServers,
8+
} from '@repo/mcp-common/src/cloudflare-oauth-handler'
9+
import { McpError } from '@repo/mcp-common/src/mcp-error'
10+
11+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
12+
export const getApp = () =>
13+
new Hono()
14+
/**
15+
* OAuth Callback Endpoint
16+
*
17+
* This route handles the callback from Cloudflare after user authentication.
18+
* It then proceeds to redirect the user to a valid server callback path (e.g /workers/observability/callback)
19+
*/
20+
.get('/oauth/callback', zValidator('query', AuthQuery), async (c) => {
21+
try {
22+
const { state, code, scope } = c.req.valid('query')
23+
const oauthReqInfo = AuthRequestSchemaWithExtraParams.parse(atob(state))
24+
if (!oauthReqInfo.clientId) {
25+
throw new McpError('Invalid State', 400)
26+
}
27+
const params = new URLSearchParams({
28+
code,
29+
state,
30+
scope,
31+
})
32+
33+
if (!ValidServers.safeParse(oauthReqInfo.serverPath).success) {
34+
throw new McpError(`Invalid server redirect ${oauthReqInfo.serverPath}`, 400)
35+
}
36+
37+
const redirectUrl = new URL(
38+
`${new URL(c.req.url).origin}/${oauthReqInfo.serverPath}/oauth/callback?${params.toString()}`
39+
)
40+
return Response.redirect(redirectUrl.toString(), 302)
41+
} catch (e) {
42+
console.error(e)
43+
if (e instanceof McpError) {
44+
return c.text(e.message, { status: e.code })
45+
}
46+
return c.text('Internal Error', 500)
47+
}
48+
})
49+
50+
export default {
51+
fetch: getApp().fetch,
52+
} satisfies ExportedHandler

apps/auth-server/tsconfig.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": "@repo/typescript-config/workers.json"
3+
}

apps/auth-server/types.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { TestEnv } from './vitest.config'
2+
3+
declare module 'cloudflare:test' {
4+
interface ProvidedEnv extends TestEnv {}
5+
}

0 commit comments

Comments
 (0)