different-ai/openwork · error · EnterpriseMcpOAuthContractError

MCP_OAUTH_CONFIGURATION_REQUIRED

MCP_OAUTH_CONFIGURATION_REQUIRED

Error message

The OAuth authorization response cannot be validated without a bound authorization-server issuer.

What it means

validateMcpAuthorizationResponseIssuer needs an expected authorization-server issuer to bind the OAuth authorization response against. The expected issuer can come from input.expectedIssuer, the authorization-server metadata's issuer field, or stored discovery's authorizationServerUrl; if none is available, the contract cannot be enforced and MCP_OAUTH_CONFIGURATION_REQUIRED is thrown. This is a configuration gap, not a mismatch.

Source

Thrown at packages/enterprise-mcp-client/src/authorization-response.ts:52

  expectedIssuer?: string | null
  discoveryState?: unknown
  /** Undefined means the response omitted `iss`; an empty value is present and invalid. */
  responseIssuer?: string
  mixUpDefense?: McpAuthorizationResponseMixUpDefense
}): McpAuthorizationResponseIssuerValidation {
  const discovery = isRecord(input.discoveryState) ? input.discoveryState : undefined
  const authorizationServerMetadata = isRecord(discovery?.authorizationServerMetadata)
    ? discovery.authorizationServerMetadata
    : undefined
  const resourceMetadata = isRecord(discovery?.resourceMetadata)
    ? discovery.resourceMetadata
    : undefined
  const metadataIssuer = optionalString(authorizationServerMetadata?.issuer)
  const discoveryIssuer = optionalString(discovery?.authorizationServerUrl)
  const expectedIssuer = input.expectedIssuer ?? metadataIssuer ?? discoveryIssuer

  if (!expectedIssuer) {
    throw new EnterpriseMcpOAuthContractError(
      "MCP_OAUTH_CONFIGURATION_REQUIRED",
      "The OAuth authorization response cannot be validated without a bound authorization-server issuer.",
    )
  }

  const advertisedIssuers = Array.isArray(resourceMetadata?.authorization_servers)
    ? resourceMetadata.authorization_servers.filter((value): value is string => typeof value === "string")
    : undefined
  const discoveryIsBound = discoveryIssuer
    ? isAuthorizationServerDiscoveryBound({
        authorizationServerUrl: discoveryIssuer,
        authorizationServerMetadata: metadataIssuer ? { issuer: metadataIssuer } : undefined,
        resourceMetadata: resourceMetadata
          ? {
              resource: optionalString(resourceMetadata.resource),
              authorization_servers: advertisedIssuers,
            }
          : undefined,

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Run authorization-server metadata discovery first and pass its issuer as input.expectedIssuer
  2. Ensure the stored MCP connection has a discovery record containing authorizationServerUrl before validating responses
  3. Pass expectedIssuer explicitly when metadata/discovery are not available
  4. Fix the metadata endpoint or its caching so issuer is present in authorizationServerMetadata

Example fix

// before
validateMcpAuthorizationResponseIssuer({ response, resourceMetadata }) // no issuer source
// after
const metadata = await discoverAuthorizationServerMetadata(issuerUrl)
validateMcpAuthorizationResponseIssuer({ response, resourceMetadata, expectedIssuer: metadata.issuer })
Defensive patterns

Strategy: validation

Validate before calling

function assertIssuerSource(input) {
  const has = input.expectedIssuer
    || (input.authorizationServerMetadata && input.authorizationServerMetadata.issuer)
    || (input.discovery && input.discovery.authorizationServerUrl);
  if (!has) throw new Error('Provide expectedIssuer, metadata.issuer, or discovery.authorizationServerUrl before validating the authorization response');
}

Type guard

function hasExpectedIssuer(input: ValidationInput): input is ValidationInput & { expectedIssuer: string } {
  return typeof input.expectedIssuer === 'string' && input.expectedIssuer.length > 0;
}

Try / catch

try {
  validateMcpAuthorizationResponseIssuer(input)
} catch (e) {
  if (e instanceof EnterpriseMcpOAuthContractError && e.code === 'MCP_OAUTH_CONFIGURATION_REQUIRED') {
    // run metadata discovery and retry with expectedIssuer
  } else throw e
}

Prevention

When it happens

Trigger: Calling validateMcpAuthorizationResponseIssuer (during OAuth response validation) when input.expectedIssuer is undefined, authorizationServerMetadata is absent or lacks issuer, and discovery has no authorizationServerUrl.

Common situations: Skipping server metadata discovery before processing the callback; a stored MCP connection missing its discovery record (e.g. after DB migration or partial save); constructing validation input manually without passing expectedIssuer; metadata endpoints that return a payload without an issuer field.

Related errors


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