fix(oauth): accept application/x-www-form-urlencoded on /oauth/token
All checks were successful
Deploy to Production / deploy (push) Successful in 1m24s
All checks were successful
Deploy to Production / deploy (push) Successful in 1m24s
Sovereign-audit traced "Authorization with the MCP server failed" past discovery, DCR, /authorize → redirect → code, and into POST /oauth/token, which Fastify rejected with 415 before our handler ever ran. RFC 6749 §3.2 makes form-urlencoded the mandatory wire format for the token endpoint, and every DCR-emitting client (Claude Desktop, Cursor, OpenAI Codex, …) posts it that way. Fastify ships no built-in parser for that media type so the route 415'd from the framework's content- type layer — invisible to a code review of the route handler. Adds a small URLSearchParams-based parser next to the existing JSON one, parses the form body into a plain object so the route's zod schema picks it up unchanged. No new dependency.
This commit is contained in:
parent
0c6d738a6b
commit
44cebc9fd8
@ -53,6 +53,29 @@ app.addContentTypeParser(
|
||||
},
|
||||
);
|
||||
|
||||
// RFC 6749 §3.2 makes application/x-www-form-urlencoded the mandatory wire
|
||||
// format for the OAuth token endpoint, and most DCR-emitting clients
|
||||
// (Claude Desktop included) post it that way without negotiating. Fastify
|
||||
// has no built-in parser for it, so without this every POST /oauth/token
|
||||
// hit 415 before reaching our handler. Parsed into a plain object so the
|
||||
// existing zod schemas don't need to change.
|
||||
app.addContentTypeParser(
|
||||
'application/x-www-form-urlencoded',
|
||||
{ parseAs: 'string' },
|
||||
(_req, body, done) => {
|
||||
const text = body as string;
|
||||
if (!text) return done(null, {});
|
||||
try {
|
||||
const params = new URLSearchParams(text);
|
||||
const out: Record<string, string> = {};
|
||||
for (const [k, v] of params) out[k] = v;
|
||||
done(null, out);
|
||||
} catch (err) {
|
||||
done(err as Error, undefined);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await app.register(cors, {
|
||||
origin: [config.NEXT_PUBLIC_APP_URL],
|
||||
credentials: true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user