amruthpillai/reactive-resume · error · AuthError
AuthError
AuthError
Error message
Unauthorized
What it means
Thrown at the end of authenticateRequest after both credential paths have been tried and neither produced a valid principal. The function first attempts a Bearer OAuth token (authorization header) via verifyOAuthToken, then an x-api-key header via auth.api.verifyApiKey; if both are absent or invalid it throws AuthError (message 'Unauthorized'). It is the single gate for MCP HTTP requests, so any MCP client without a valid token or key is rejected.
Source
Thrown at apps/server/src/mcp/auth.ts:32
const payload = await verifyOAuthToken(authHeader.slice(7));
if (payload?.sub) return;
} catch {
// Invalid or expired token; fall through to API key auth.
}
}
const apiKey = request.headers.get("x-api-key");
if (apiKey) {
try {
const result = await auth.api.verifyApiKey({ body: { key: apiKey } });
if (result.valid) return;
} catch {
// Invalid or malformed key; fall through to AuthError.
}
}
throw new AuthError();
}
View on GitHub (pinned to 3a5b12e2a4)
Solutions
- Provide a Better Auth API key: send header 'x-api-key: <key>' generated from the app's API-key endpoint.
- Or provide a valid session: send 'Authorization: Bearer <better-auth-session-token>' obtained by logging in through the web app.
- Verify the header name is exactly 'x-api-key' (case-insensitive for header lookup, but spelling must match) and that the key is not wrapped in quotes or prefixed with 'Bearer '.
- If using a Bearer token, confirm AUTH_SECRET on the server matches the secret that minted the token (mismatch silently invalidates the JWT).
Example fix
// before
await fetch(mcpUrl, { headers: { 'api-key': apiKey } });
// after
await fetch(mcpUrl, { headers: { 'x-api-key': apiKey } }); Defensive patterns
Strategy: validation
Validate before calling
// Validate the request will authenticate before sending.
function buildAuthHeaders(apiKey?: string, bearerToken?: string): Record<string, string> {
if (bearerToken && bearerToken.startsWith('Bearer ')) return { authorization: bearerToken };
if (bearerToken && bearerToken.length > 0) return { authorization: `Bearer ${bearerToken}` };
if (apiKey && apiKey.trim().length > 0) return { 'x-api-key': apiKey.trim() };
throw new Error('MCP client must provide either a Better Auth bearer token or an x-api-key.');
} Type guard
function hasMcpCredentials(opts: { apiKey?: string; bearerToken?: string }): boolean {
return (!!opts.bearerToken && opts.bearerToken.length > 0) || (!!opts.apiKey && opts.apiKey.trim().length > 0);
} Prevention
- Centralize MCP header construction in one client wrapper so every call carries valid auth.
- Treat HTTP 401 from the MCP endpoint as 'credentials invalid' and surface a re-login/re-key prompt rather than retrying.
- Store the API key in the platform secret store, never in source.
- When rotating AUTH_SECRET, invalidate and reissue all API keys and bearer tokens.
When it happens
Trigger: Calling any MCP endpoint (e.g. POST to the MCP transport route mounted in apps/server) with no Authorization and no x-api-key header; sending an expired/revoked Better Auth session bearer token; sending a malformed or deleted API key in x-api-key; sending a key with the wrong header name (e.g. 'api-key' instead of 'x-api-key').
Common situations: Local dev without logging in first; MCP client config (claude_desktop_config.json / cursor mcp config) missing the apiKey field; the user logged out and the cached bearer token expired; Better Auth API key was rotated or deleted server-side; reverse proxy stripping the Authorization header.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- BAD_REQUEST
- ${providerName} provider did not return an email address. Th
- Invalid resume URI — expected format: resume://{id}
- Application documents must be PDF files.
- Application document cannot be empty.
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/0369b23c5843b0c5.
Report an issue: GitHub.