Skip to content

Commit eff548c

Browse files
committed
adjust to provided resource
1 parent 87da0e0 commit eff548c

File tree

2 files changed

+67
-3
lines changed

2 files changed

+67
-3
lines changed

src/client/auth.test.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,5 +1045,66 @@ describe("OAuth Authorization", () => {
10451045
"https://different-resource.example.com/mcp-server"
10461046
);
10471047
});
1048+
1049+
it("uses prefix of server URL from PRM resource as resource parameter", async () => {
1050+
// Mock successful metadata discovery with resource URL that is a prefix of requested URL
1051+
mockFetch.mockImplementation((url) => {
1052+
const urlString = url.toString();
1053+
1054+
if (urlString.includes("/.well-known/oauth-protected-resource")) {
1055+
return Promise.resolve({
1056+
ok: true,
1057+
status: 200,
1058+
json: async () => ({
1059+
// Resource is a prefix of the requested server URL
1060+
resource: "https://api.example.com/",
1061+
authorization_servers: ["https://auth.example.com"],
1062+
}),
1063+
});
1064+
} else if (urlString.includes("/.well-known/oauth-authorization-server")) {
1065+
return Promise.resolve({
1066+
ok: true,
1067+
status: 200,
1068+
json: async () => ({
1069+
issuer: "https://auth.example.com",
1070+
authorization_endpoint: "https://auth.example.com/authorize",
1071+
token_endpoint: "https://auth.example.com/token",
1072+
response_types_supported: ["code"],
1073+
code_challenge_methods_supported: ["S256"],
1074+
}),
1075+
});
1076+
}
1077+
1078+
return Promise.resolve({ ok: false, status: 404 });
1079+
});
1080+
1081+
// Mock provider methods
1082+
(mockProvider.clientInformation as jest.Mock).mockResolvedValue({
1083+
client_id: "test-client",
1084+
client_secret: "test-secret",
1085+
});
1086+
(mockProvider.tokens as jest.Mock).mockResolvedValue(undefined);
1087+
(mockProvider.saveCodeVerifier as jest.Mock).mockResolvedValue(undefined);
1088+
(mockProvider.redirectToAuthorization as jest.Mock).mockResolvedValue(undefined);
1089+
1090+
// Call auth with a URL that has the resource as prefix
1091+
const result = await auth(mockProvider, {
1092+
serverUrl: "https://api.example.com/mcp-server/endpoint",
1093+
});
1094+
1095+
expect(result).toBe("REDIRECT");
1096+
1097+
// Verify the authorization URL includes the resource parameter from PRM
1098+
expect(mockProvider.redirectToAuthorization).toHaveBeenCalledWith(
1099+
expect.objectContaining({
1100+
searchParams: expect.any(URLSearchParams),
1101+
})
1102+
);
1103+
1104+
const redirectCall = (mockProvider.redirectToAuthorization as jest.Mock).mock.calls[0];
1105+
const authUrl: URL = redirectCall[0];
1106+
// Should use the PRM's resource value, not the full requested URL
1107+
expect(authUrl.searchParams.get("resource")).toBe("https://api.example.com/");
1108+
});
10481109
});
10491110
});

src/client/auth.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,12 +197,15 @@ export async function auth(
197197
return "REDIRECT";
198198
}
199199

200-
async function selectResourceURL(serverUrl: string| URL, provider: OAuthClientProvider, resourceMetadata?: OAuthProtectedResourceMetadata): Promise<URL | undefined> {
201-
const resource = resourceUrlFromServerUrl(serverUrl);
200+
export async function selectResourceURL(serverUrl: string| URL, provider: OAuthClientProvider, resourceMetadata?: OAuthProtectedResourceMetadata): Promise<URL | undefined> {
201+
let resource = resourceUrlFromServerUrl(serverUrl);
202202
if (provider.validateResourceURL) {
203203
return await provider.validateResourceURL(resource, resourceMetadata?.resource);
204204
} else if (resourceMetadata) {
205-
if (!checkResourceAllowed({ requestedResource: resource, configuredResource: resourceMetadata.resource })) {
205+
if (checkResourceAllowed({ requestedResource: resource, configuredResource: resourceMetadata.resource })) {
206+
// If the resource mentioned in metadata is valid, prefer it since it is what the server is telling us to request.
207+
resource = new URL(resourceMetadata.resource);
208+
} else {
206209
throw new Error(`Protected resource ${resourceMetadata.resource} does not match expected ${resource} (or origin)`);
207210
}
208211
}

0 commit comments

Comments
 (0)