mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-21 22:11:42 +10:00
fix(auth): use loopback URL for MCP OAuth JWKS verification (#3297)
* fix(auth): use loopback URL for MCP OAuth JWKS verification Fetch the JWKS endpoint over the internal loopback address instead of the public APP_URL, so token verification works under Docker port-mapping, reverse proxies, and other deployments where the public URL does not loop back to the Node process. Also log the specific MCP OAuth verification error instead of swallowing it with a bare catch. Fixes #3077 * fix(auth): normalize internal JWKS URL and throttle MCP OAuth warnings - Problem: default loopback JWKS URL used PORT in dev where the server listens on SERVER_PORT (3001), and trailing-slash overrides produced //api/auth/jwks; unthrottled warn logs could flood on bad bearer tokens. - Fix: resolveInternalBaseUrl trims/normalizes BETTER_AUTH_INTERNAL_URL, mirrors apps/server listen-port selection, and MCP OAuth warnings are throttled to once per minute. - Verification: pnpm exec biome check on changed files; pnpm typecheck. * fix(auth): declare BETTER_AUTH_INTERNAL_URL in turbo globalEnv - Problem: Turborepo strict env mode strips undeclared BETTER_AUTH_INTERNAL_URL under pnpm dev, so the JWKS override silently falls back to loopback. - Fix: add BETTER_AUTH_INTERNAL_URL to turbo.json globalEnv (required for any new env var per CLAUDE.md). - Verification: python3 JSON parse of turbo.json; confirmed var was absent from globalEnv before this change. --------- Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
This commit is contained in:
co-authored by
Amruth Pillai
parent
5fc9c3ee04
commit
7eb6d3bdbf
@@ -1,5 +1,21 @@
|
||||
import { auth, verifyOAuthToken } from "@reactive-resume/auth/config";
|
||||
|
||||
const OAUTH_WARN_THROTTLE_MS = 60_000;
|
||||
let lastOAuthWarnAt = 0;
|
||||
|
||||
function warnOAuthThrottled(message: string, detail?: unknown): void {
|
||||
const now = Date.now();
|
||||
if (now - lastOAuthWarnAt < OAUTH_WARN_THROTTLE_MS) return;
|
||||
lastOAuthWarnAt = now;
|
||||
|
||||
if (detail !== undefined) {
|
||||
console.warn(message, detail);
|
||||
return;
|
||||
}
|
||||
|
||||
console.warn(message);
|
||||
}
|
||||
|
||||
export class AuthError extends Error {
|
||||
constructor() {
|
||||
super("Unauthorized");
|
||||
@@ -13,8 +29,9 @@ export async function authenticateRequest(request: Request): Promise<void> {
|
||||
try {
|
||||
const payload = await verifyOAuthToken(authHeader.slice(7));
|
||||
if (payload?.sub) return;
|
||||
} catch {
|
||||
// Invalid or expired token; fall through to API key auth.
|
||||
warnOAuthThrottled("[MCP] OAuth token verified but missing `sub` claim");
|
||||
} catch (error) {
|
||||
warnOAuthThrottled("[MCP] OAuth token verification failed:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user