Skip to content

fix: preserve auth server path in OAuth metadata URL construction #521

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions client/src/components/OAuthFlowProgress.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Button } from "./ui/button";
import { DebugInspectorOAuthClientProvider } from "@/lib/auth";
import { useEffect, useMemo, useState } from "react";
import { OAuthClientInformation } from "@modelcontextprotocol/sdk/shared/auth.js";
import { oauthAuthServerMetadataUrl } from "@/utils/oauthUtils.ts";

interface OAuthStepProps {
label: string;
Expand Down Expand Up @@ -196,10 +197,7 @@ export const OAuthFlowProgress = ({
<p className="text-xs text-muted-foreground">
From{" "}
{
new URL(
"/.well-known/oauth-authorization-server",
authState.authServerUrl,
).href
oauthAuthServerMetadataUrl(authState.authServerUrl).href
}
</p>
)}
Expand Down
35 changes: 35 additions & 0 deletions client/src/utils/__tests__/oauthUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
generateOAuthErrorDescription,
parseOAuthCallbackParams,
oauthAuthServerMetadataUrl,
} from "@/utils/oauthUtils.ts";

describe("parseOAuthCallbackParams", () => {
Expand Down Expand Up @@ -76,3 +77,37 @@ describe("generateOAuthErrorDescription", () => {
);
});
});

describe("oauthAuthServerMetadataUrl", () => {
it("Returns metadata URL for simple auth server URL", () => {
const input = new URL("https://auth.example.com");
const result = oauthAuthServerMetadataUrl(input);
expect(result.href).toBe(
"https://auth.example.com/.well-known/oauth-authorization-server",
);
});

it("Returns metadata URL for auth server with path", () => {
const input = new URL("https://auth.example.com/oauth/tenant/xyz");
const result = oauthAuthServerMetadataUrl(input);
expect(result.href).toBe(
"https://auth.example.com/.well-known/oauth-authorization-server/oauth/tenant/xyz",
);
});

it("Strips trailing slash from path as per spec", () => {
const input = new URL("https://auth.example.com/oauth/");
const result = oauthAuthServerMetadataUrl(input);
expect(result.href).toBe(
"https://auth.example.com/.well-known/oauth-authorization-server/oauth",
);
});

it("Handles auth server URL with port", () => {
const input = new URL("https://auth.example.com:8080");
const result = oauthAuthServerMetadataUrl(input);
expect(result.href).toBe(
"https://auth.example.com:8080/.well-known/oauth-authorization-server",
);
});
});
16 changes: 16 additions & 0 deletions client/src/utils/oauthUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,19 @@ export const generateOAuthErrorDescription = (
.filter(Boolean)
.join("\n");
};

/**
* Build the well-known OAuth 2.0 metadata URL from an authServerUrl.
* Handles auth server paths per RFC 8414 §3.1.
*
* @param {URL} authServerUrl e.g. new URL("https://my.auth-server.com/oauth/tenant/xyz")
* @returns {URL} e.g. new URL("https://my.auth-server.com/.well-known/oauth-authorization-server/oauth/tenant/xyz")
*/
export const oauthAuthServerMetadataUrl = (authServerUrl: URL): URL => {
// Strip a trailing slash from the path (required by the spec)
const path = authServerUrl.pathname.replace(/\/$/, "");

return new URL(
`${authServerUrl.origin}/.well-known/oauth-authorization-server${path}`,
);
};