mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-03 10:13:47 +10:00
fix: restore MCP OAuth registration and authorization (#3421)
* fix: align MCP OAuth provider schema and authorization flow * test: isolate OpenAPI generation from OAuth initialization * fix: accept auth routes without a callback query * fix: require explicit OAuth consent and preserve signed requests * test: verify OAuth audiences through real MCP initialization * test(e2e): isolate OAuth token audience validation
This commit is contained in:
@@ -46,6 +46,9 @@ function resolveInternalBaseUrl(): string {
|
||||
const internalBaseUrl = resolveInternalBaseUrl();
|
||||
|
||||
const oauthAudienceBase = authBaseUrl.replace(/\/$/, "");
|
||||
// These identify the same account-wide API/MCP resource, not separate permission
|
||||
// tiers. Protected-resource metadata advertises the root; MCP clients may also
|
||||
// select the mounted endpoint or normalize either URI with a trailing slash.
|
||||
const OAUTH_AUDIENCES = [
|
||||
oauthAudienceBase,
|
||||
`${oauthAudienceBase}/`,
|
||||
@@ -301,11 +304,12 @@ const getAuthConfig = () => {
|
||||
}),
|
||||
oauthProvider({
|
||||
loginPage: "/api/auth/oauth",
|
||||
consentPage: "/api/auth/oauth",
|
||||
validAudiences: OAUTH_AUDIENCES,
|
||||
consentPage: "/auth/consent",
|
||||
resources: OAUTH_AUDIENCES,
|
||||
clientRegistrationDefaultResources: OAUTH_AUDIENCES,
|
||||
allowDynamicClientRegistration: true,
|
||||
// Required for MCP client onboarding (RFC 7591). Phishing vector is closed by the
|
||||
// redirect_uri policy in the hooks.before middleware above and server auth preflight.
|
||||
// Required for MCP client onboarding (RFC 7591). Redirect URI validation
|
||||
// and explicit user consent protect access by dynamically registered clients.
|
||||
allowUnauthenticatedClientRegistration: true,
|
||||
rateLimit: oauthProviderRateLimit,
|
||||
silenceWarnings: { oauthAuthServerConfig: true },
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { oauthProvider } from "@better-auth/oauth-provider";
|
||||
import { getTableColumns, is, Table } from "drizzle-orm";
|
||||
import * as dbSchema from "@reactive-resume/db/schema";
|
||||
|
||||
const plugin = oauthProvider({ loginPage: "/login", consentPage: "/consent" });
|
||||
|
||||
describe("OAuth provider persistence schema", () => {
|
||||
it.each(Object.entries(plugin.schema))("provides every installed plugin field for %s", (modelName, model) => {
|
||||
const table = Reflect.get(dbSchema, modelName);
|
||||
expect(is(table, Table), `Missing table ${modelName}`).toBe(true);
|
||||
if (!is(table, Table)) return;
|
||||
const columns = getTableColumns(table);
|
||||
for (const field of Object.keys(model.fields)) {
|
||||
expect(columns, `${modelName}.${field}`).toHaveProperty(field);
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user