Fix OAuth discovery for servers with non-root resource paths
When the resource URL has a sub-path (e.g. https://mcp.sentry.dev/mcp), the RFC 8414 path-suffixed well-known URL may not exist. Fall back to the root well-known URL before giving up. This matches OpenCode core's behavior and fixes authentication for servers like Sentry that serve OAuth metadata only at the root path.
This commit is contained in:
@@ -36,28 +36,16 @@ async function fetchMetadata(url: string): Promise<{ ok: true; json: Record<stri
|
||||
return { ok: true, json }
|
||||
}
|
||||
|
||||
async function fetchAuthorizationServerMetadata(issuer: string, resource: string): Promise<OAuthServerMetadata> {
|
||||
const issuerUrl = parseHttpsUrl(issuer, "Authorization server URL")
|
||||
const issuerPath = issuerUrl.pathname.replace(/\/+$/, "")
|
||||
const metadataUrl = new URL(`/.well-known/oauth-authorization-server${issuerPath}`, issuerUrl).toString()
|
||||
const metadata = await fetchMetadata(metadataUrl)
|
||||
|
||||
if (!metadata.ok) {
|
||||
if (metadata.status === 404) {
|
||||
throw new Error("OAuth authorization server metadata not found")
|
||||
}
|
||||
throw new Error(`OAuth authorization server metadata fetch failed (${metadata.status})`)
|
||||
}
|
||||
|
||||
function parseMetadataFields(json: Record<string, unknown>, resource: string): OAuthServerMetadata {
|
||||
const authorizationEndpoint = parseHttpsUrl(
|
||||
readStringField(metadata.json, "authorization_endpoint"),
|
||||
readStringField(json, "authorization_endpoint"),
|
||||
"authorization_endpoint"
|
||||
).toString()
|
||||
const tokenEndpoint = parseHttpsUrl(
|
||||
readStringField(metadata.json, "token_endpoint"),
|
||||
readStringField(json, "token_endpoint"),
|
||||
"token_endpoint"
|
||||
).toString()
|
||||
const registrationEndpointValue = metadata.json.registration_endpoint
|
||||
const registrationEndpointValue = json.registration_endpoint
|
||||
const registrationEndpoint =
|
||||
typeof registrationEndpointValue === "string" && registrationEndpointValue.length > 0
|
||||
? parseHttpsUrl(registrationEndpointValue, "registration_endpoint").toString()
|
||||
@@ -71,6 +59,29 @@ async function fetchAuthorizationServerMetadata(issuer: string, resource: string
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchAuthorizationServerMetadata(issuer: string, resource: string): Promise<OAuthServerMetadata> {
|
||||
const issuerUrl = parseHttpsUrl(issuer, "Authorization server URL")
|
||||
const issuerPath = issuerUrl.pathname.replace(/\/+$/, "")
|
||||
const metadataUrl = new URL(`/.well-known/oauth-authorization-server${issuerPath}`, issuerUrl).toString()
|
||||
const metadata = await fetchMetadata(metadataUrl)
|
||||
|
||||
if (!metadata.ok) {
|
||||
if (metadata.status === 404 && issuerPath !== "") {
|
||||
const rootMetadataUrl = new URL("/.well-known/oauth-authorization-server", issuerUrl).toString()
|
||||
const rootMetadata = await fetchMetadata(rootMetadataUrl)
|
||||
if (rootMetadata.ok) {
|
||||
return parseMetadataFields(rootMetadata.json, resource)
|
||||
}
|
||||
}
|
||||
if (metadata.status === 404) {
|
||||
throw new Error("OAuth authorization server metadata not found")
|
||||
}
|
||||
throw new Error(`OAuth authorization server metadata fetch failed (${metadata.status})`)
|
||||
}
|
||||
|
||||
return parseMetadataFields(metadata.json, resource)
|
||||
}
|
||||
|
||||
function parseAuthorizationServers(metadata: Record<string, unknown>): string[] {
|
||||
const servers = metadata.authorization_servers
|
||||
if (!Array.isArray(servers)) return []
|
||||
|
||||
Reference in New Issue
Block a user