nexu-io/open-design · error · Error

could not discover OAuth metadata for ${issuer}

Error message

could not discover OAuth metadata for ${issuer}

What it means

beginAuth Step 2 calls discoverAuthServer(issuer) and throws if it returns null, meaning no valid OAuth Authorization Server metadata could be fetched or parsed at the issuer (the /.well-known/oauth-authorization-server or RFC8414 fallback). Step 1 may have derived the issuer from the resource origin when the server published no protected-resource metadata.

Source

Thrown at apps/daemon/src/mcp-oauth.ts:553

 * correct authorize URL, and (b) finish the flow when the callback hits.
 */
export async function beginAuth(
  input: BeginAuthInput,
): Promise<BeginAuthResult> {
  const fetchImpl = input.fetchImpl ?? fetch;

  // Step 1: ask the MCP server who its auth server is. If the server
  // doesn't publish protected-resource metadata, fall back to assuming
  // the resource origin IS the auth server — most "stand-alone" MCP
  // providers (Higgsfield etc.) host both at the same host.
  const prm = await discoverProtectedResource(input.serverUrl, fetchImpl);
  const issuerHint = prm?.authorization_servers?.[0];
  const issuer = issuerHint ?? new URL(input.serverUrl).origin;

  // Step 2: discovery on the auth server.
  const authServer = await discoverAuthServer(issuer, fetchImpl);
  if (!authServer) {
    throw new Error(`could not discover OAuth metadata for ${issuer}`);
  }

  // Step 3: ensure we have a registered client_id (DCR if missing).
  const client = await getOrRegisterClient(
    input.dataDir,
    authServer,
    input.redirectUri,
    fetchImpl,
  );

  // Step 4: PKCE + state.
  const codeVerifier = generateCodeVerifier();
  const codeChallenge = deriveCodeChallenge(codeVerifier);
  const state = generateState();

  const scope =
    input.scope ??
    (Array.isArray(prm?.scopes_supported) && prm!.scopes_supported!.length > 0

View on GitHub (pinned to 5be4028344)

Solutions

  1. Confirm serverUrl is correct and the auth server is discoverable at its issuer.
  2. Ensure the MCP server publishes protected-resource metadata pointing at the real auth server via authorization_servers.
  3. Check network or proxy access to /.well-known/oauth-authorization-server.
Defensive patterns

Strategy: try-catch

Validate before calling

const issuer = new URL(serverUrl).origin;
const meta = await discoverAuthServer(issuer);
if (!meta) {
  throw new Error('no OAuth metadata reachable; verify serverUrl and protected-resource metadata');
}

Try / catch

try {
  await beginAuth(input);
} catch (e) {
  if (/could not discover OAuth metadata/i.test(e.message)) {
    // verify serverUrl; ensure protected-resource metadata advertises the auth server
    surfaceToUser('Check the MCP server URL and its auth server discovery.');
  }
  throw e;
}

Prevention

When it happens

Trigger: The issuer is unreachable or is not an auth server; the well-known endpoint returns 404; the metadata JSON is malformed; the MCP server's origin is not its auth server and no authorization_servers hint was published.

Common situations: A provider hosts auth on a different host but did not publish protected-resource metadata; a typo in serverUrl; a corporate network blocks the well-known path.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/c85ecded4a9dfb72. Report an issue: GitHub.