mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-22 14:51:45 +00:00
fix: open CORS for OAuth register/authorize + correct WWW-Authenticate PRM path
Two follow-up fixes after the SDK auth migration:
1. CORS for browser-based OAuth clients (ChatGPT DCR 403)
The global cors({ origin: false }) intercepts OPTIONS preflight for
/oauth/register and /oauth/authorize before the SDK's own cors()
middleware inside clientRegistrationHandler/authorizationHandler
runs, causing the browser to reject the response with no
Access-Control-Allow-Origin header. ChatGPT's connector makes DCR
from the browser, so this manifested as a 403.
Fix: extend the open-CORS pre-middleware to also cover
/oauth/register and /oauth/authorize (same pattern as /.well-known).
2. WWW-Authenticate resource_metadata URL (RFC 9728 §5)
The MCP handler was advertising the base PRM path
(/.well-known/oauth-protected-resource) instead of the path-aware
variant (/.well-known/oauth-protected-resource/mcp). RFC 9728
requires the resource path to be appended when the resource URI has
a path component. The SDK registers the path-aware URL; the
WWW-Authenticate header now points to the same location.
This commit is contained in:
+11
-3
@@ -95,11 +95,19 @@ export function createApp(): express.Application {
|
|||||||
const hstsActive = shouldForceHttps || process.env.NODE_ENV === 'production';
|
const hstsActive = shouldForceHttps || process.env.NODE_ENV === 'production';
|
||||||
const hstsIncludeSubdomains = process.env.HSTS_INCLUDE_SUBDOMAINS === 'true';
|
const hstsIncludeSubdomains = process.env.HSTS_INCLUDE_SUBDOMAINS === 'true';
|
||||||
|
|
||||||
// RFC 8414 / RFC 9728: discovery docs are world-readable — open CORS regardless of deployment config
|
// RFC 8414 / RFC 9728 / RFC 7591: discovery docs and DCR are world-readable/writable —
|
||||||
// Covers both the base path and the RFC 9728 path-based variant (/.well-known/oauth-protected-resource/mcp)
|
// open CORS for external MCP clients regardless of the deployment's ALLOWED_ORIGINS config.
|
||||||
|
// /oauth/register and /oauth/authorize need it because browser-based clients (ChatGPT, etc.)
|
||||||
|
// send a CORS preflight that the global cors({ origin: false }) would answer WITHOUT
|
||||||
|
// Access-Control-Allow-Origin, causing the browser to reject the response before the
|
||||||
|
// SDK's own cors() middleware inside clientRegistrationHandler/authorizationHandler runs.
|
||||||
app.use(
|
app.use(
|
||||||
(req: Request, _res: Response, next: NextFunction) => {
|
(req: Request, _res: Response, next: NextFunction) => {
|
||||||
if (req.path.startsWith('/.well-known/oauth-')) {
|
if (
|
||||||
|
req.path.startsWith('/.well-known/oauth-') ||
|
||||||
|
req.path === '/oauth/register' ||
|
||||||
|
req.path === '/oauth/authorize'
|
||||||
|
) {
|
||||||
cors({ origin: '*', credentials: false })(req, _res, next);
|
cors({ origin: '*', credentials: false })(req, _res, next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
|
|||||||
@@ -154,8 +154,9 @@ sessionSweepInterval.unref();
|
|||||||
|
|
||||||
function setAuthChallenge(res: Response, error = 'invalid_token'): void {
|
function setAuthChallenge(res: Response, error = 'invalid_token'): void {
|
||||||
const base = (getAppUrl() || '').replace(/\/+$/, '');
|
const base = (getAppUrl() || '').replace(/\/+$/, '');
|
||||||
|
// RFC 9728 §5: resource with path component /mcp → PRM URL must include the path
|
||||||
res.set('WWW-Authenticate',
|
res.set('WWW-Authenticate',
|
||||||
`Bearer realm="TREK MCP", resource_metadata="${base}/.well-known/oauth-protected-resource", error="${error}"`);
|
`Bearer realm="TREK MCP", resource_metadata="${base}/.well-known/oauth-protected-resource/mcp", error="${error}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VerifyTokenResult {
|
interface VerifyTokenResult {
|
||||||
|
|||||||
Reference in New Issue
Block a user