different-ai/openwork · error · ProbeFailure

AUTH_ISSUER_DISCOVERY

AUTH_ISSUER_DISCOVERY

Error message

Protected-resource metadata had no authorization server

What it means

The protected-resource metadata must list at least one authorization_servers entry; the probe takes the first one to discover the issuer's OAuth metadata. An empty or missing authorization_servers array leaves the client with no authorization server to authenticate against, so the probe fails in AUTH_ISSUER_DISCOVERY with subcode oauth_discovery_issuer.

Source

Thrown at packages/enterprise-mcp-mock-server/src/testing/probe.ts:554

      await fetchStep(resourceMetadataUrl, undefined, "AUTH_RESOURCE_DISCOVERY", overallDeadline),
      "AUTH_RESOURCE_DISCOVERY",
    )
    const resourceMetadata = parseAt(
      protectedResourceMetadataSchema,
      await parseJson(resourceResponse, "AUTH_RESOURCE_DISCOVERY", "oauth_discovery_resource"),
      "AUTH_RESOURCE_DISCOVERY",
      "oauth_discovery_resource",
      "Protected-resource metadata did not match the required shape",
    )
    if (resourceMetadata.resource !== mcpUrl) {
      throw new ProbeFailure("AUTH_RESOURCE_DISCOVERY", "oauth_discovery_resource", "Protected-resource metadata did not identify this MCP endpoint")
    }
    recordPassed(phases, "AUTH_RESOURCE_DISCOVERY", startedAt, "Protected-resource metadata is coherent")

    startedAt = Date.now()
    const authorizationServerValue = resourceMetadata.authorization_servers[0]
    if (!authorizationServerValue) {
      throw new ProbeFailure("AUTH_ISSUER_DISCOVERY", "oauth_discovery_issuer", "Protected-resource metadata had no authorization server")
    }
    const issuerMetadataResponse = await expectOk(
      await fetchStep(
        new URL("/.well-known/oauth-authorization-server", assertPinnedOrigin(authorizationServerValue, baseUrl, "AUTH_ISSUER_DISCOVERY")),
        undefined,
        "AUTH_ISSUER_DISCOVERY",
        overallDeadline,
      ),
      "AUTH_ISSUER_DISCOVERY",
    )
    const issuerMetadata = parseAt(
      authorizationServerMetadataSchema,
      await parseJson(issuerMetadataResponse, "AUTH_ISSUER_DISCOVERY", "oauth_discovery_issuer"),
      "AUTH_ISSUER_DISCOVERY",
      "oauth_discovery_issuer",
      "Authorization-server metadata did not match the required shape",
    )
    if (issuerMetadata.issuer !== baseUrl.href.replace(/\/$/, "")) {

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Add "authorization_servers": ["<issuer-url>"] to the protected-resource metadata document.
  2. Wire the MCP server's OAuth configuration so it knows and advertises its authorization server.
  3. If generating metadata dynamically, guard against emitting an empty array when the AS config is missing.

Example fix

// before
{ "resource": mcpUrl }
// after
{ "resource": mcpUrl, "authorization_servers": ["https://localhost:3000/"] }
Defensive patterns

Strategy: validation

Validate before calling

const md = await (await fetch(metadataUrl)).json()
if (!Array.isArray(md.authorization_servers) || md.authorization_servers.length === 0) {
  throw new Error("authorization_servers must list at least one issuer URL")
}

Type guard

function hasAuthorizationServers(md: unknown): md is { authorization_servers: [string, ...string[]] } & Record<string, unknown> {
  return typeof md === "object" && md !== null &&
    Array.isArray((md as { authorization_servers?: unknown }).authorization_servers) &&
    (md as { authorization_servers: unknown[] }).authorization_servers.length > 0
}

Try / catch

try {
  await probeEnterpriseMcpMockServer({ baseUrl })
} catch (e) {
  if (e instanceof ProbeFailure && e.phase === "AUTH_ISSUER_DISCOVERY" && e.message.includes("no authorization server")) {
    console.error("Add authorization_servers to the protected-resource metadata")
  } else throw e
}

Prevention

When it happens

Trigger: The parsed metadata document has no authorization_servers key or an empty array (resourceMetadata.authorization_servers[0] is undefined).

Common situations: Hand-written metadata fixture omitting the field, a server emitting authorization_servers: [] when its OAuth deployment is not wired up, or a shape-validation pass that allows an empty array.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/40d580a18e6617db. Report an issue: GitHub.