Summary
In withMcpAuth (src/auth/auth-wrapper.ts), the call to the consumer-supplied verifyToken is wrapped in a try/catch that unconditionally does console.error("Unexpected error authenticating bearer token:", error) and then returns a generic 401 (line 58 on main and at v2.3.0). A second catch further down (line 102) already skips logging for OAuthError instances, but the first catch does not: any error thrown by the verifier is logged in full, typed or not.
Because the log call happens inside the library, a consumer cannot control what reaches platform logs (Vercel, CloudWatch, etc.). Throwing the library's own OAuthError from the verifier, which #63 introduced as the supported way to signal auth failure, still results in the full error object being logged before the response is built.
Why it matters
A verifyToken implementation typically calls a database or an identity provider. Driver and SDK errors routinely carry request context, SQL fragments, hostnames and sometimes connection details in their message or properties. Logging the raw object at library level makes the consumer's own error hygiene irrelevant. The only consumer-side workaround is to guarantee the verifier never throws, which conflicts with the typed-error design from #63 and means an infrastructure outage has to be presented as an invalid token (401) rather than a server error (5xx).
Reproduction
withMcpAuth(handler, async () => { throw new Error("boom: connection to db-host-1 failed: password authentication failed"); })- Send any request with a bearer token.
- Observe the full message and stack on stderr from inside
withMcpAuth, regardless of anytry/catchin the consumer's route module.
Suggested fix
Either or both:
- Respect typed errors in the verifier catch, mirroring the second catch: if
OAuthError.isInstance(error), returnbearerAuthChallengeResponse(error, challengeOptions)without logging. This makes #63's contract complete. - Log a redacted summary by default for untyped errors:
console.error("Unexpected error authenticating bearer token", { name: error?.name }), with an opt-in option onwithMcpAuth(for examplelogAuthErrors: "full" | "name" | "none", default"name") for consumers who want the full object in development. The same option would apply to theMCP handler error:log insrc/handler/mcp-api-handler.ts(line 87).
Minimal patch for (1) and the default of (2):
try {
authInfo = await verifyToken(req, bearerToken);
} catch (error) {
if (OAuthError.isInstance(error)) {
return bearerAuthChallengeResponse(error, challengeOptions);
}
console.error("Unexpected error authenticating bearer token", {
name: error instanceof Error ? error.name : typeof error,
});
return bearerAuthChallengeResponse(
new OAuthError(OAuthErrorCode.InvalidToken, "Invalid token"),
challengeOptions,
);
}Consumer-side mitigation we applied, for reference
Our verifier now returns undefined on infrastructure failures and logs { name, correlationId } itself. We accepted that a database outage therefore presents as 401 instead of 5xx. We would rather not have to.
Activity
0No activity yet.