different-ai/openwork · error · EnterpriseMcpOAuthContractError

MCP_OAUTH_AUTHORIZATION_ID_REQUIRED

MCP_OAUTH_AUTHORIZATION_ID_REQUIRED

Error message

A signed authorization transaction id is required before starting OAuth.

What it means

state() is the OAuthClientProvider hook that supplies the OAuth state parameter. It requires the current flow to be a "connect" flow carrying a signed authorizationId; in callback or runtime flows (or a connect flow missing the id) it throws EnterpriseMcpOAuthContractError with code MCP_OAUTH_AUTHORIZATION_ID_REQUIRED. The signed id is what binds the browser redirect to a durable authorization transaction; without it the library will not start OAuth.

Source

Thrown at packages/enterprise-mcp-client/src/oauth-provider.ts:140

      throw new EnterpriseMcpOAuthContractError(
        "MCP_LIFECYCLE_DEADLINE",
        "The enterprise MCP lifecycle expired before OAuth persistence could continue.",
      )
    }
    return {
      connectionId: this.connectionId,
      commitExpiresAt: this.lifecycle.expiresAt,
      signal: this.lifecycle.signal,
    }
  }

  get redirectUrl(): string {
    return this.redirectUri
  }

  state(): string {
    if (this.flow.kind !== "connect" || !this.flow.authorizationId) {
      throw new EnterpriseMcpOAuthContractError(
        "MCP_OAUTH_AUTHORIZATION_ID_REQUIRED",
        "A signed authorization transaction id is required before starting OAuth.",
      )
    }
    return this.flow.authorizationId
  }

  get clientMetadata() {
    const scope = this.requestedScopes.join(" ")
    return {
      redirect_uris: [this.redirectUri],
      client_name: this.clientName,
      grant_types: ["authorization_code", "refresh_token"],
      response_types: ["code"],
      token_endpoint_auth_method: "none",
      application_type: this.applicationType,
      ...(scope ? { scope } : {}),
    }

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Start OAuth only via client.connect() with a signed authorizationId — do not rely on runtime operations to initiate OAuth.
  2. Handle connect()'s { status: "needs_auth" } result to run the browser redirect with the properly signed state.
  3. Ensure the authorizationId is passed and non-empty when constructing a connect flow provider.
  4. If a 401 arrives during runtime calls, invalidate credentials and route the user back through connect() rather than re-authenticating in place.

Example fix

// before (initiating OAuth from a runtime session)
await auth(runtimeSession.oauthProvider, { serverUrl })
// after
const result = await client.connect({ connection, redirectUri, authorizationId: signedId })
if (result.status === "needs_auth") redirect(result.authorizeUrl)
Defensive patterns

Strategy: validation

Validate before calling

function canStartOAuth(authorizationId) {
  return typeof authorizationId === "string" && authorizationId.length > 0;
}
// require this before initiating any OAuth redirect

Type guard

function isConnectFlowWithAuthorizationId(flow: { kind: string; authorizationId?: string }): flow is { kind: "connect"; authorizationId: string } {
  return flow.kind === "connect" && typeof flow.authorizationId === "string" && flow.authorizationId.length > 0;
}

Try / catch

try {
  await client.callTool(input);
} catch (e) {
  if (e.code === "MCP_OAUTH_AUTHORIZATION_ID_REQUIRED") {
    // a 401 started OAuth outside a signed connect flow — reroute via connect() with a signed id
  }
  throw e;
}

Prevention

When it happens

Trigger: The MCP SDK's auth() helper reads provider.state() while the provider was constructed with flow { kind: "runtime" } or { kind: "callback" }, or with a connect flow whose authorizationId is undefined — e.g. reusing a runtime session's provider to initiate a new OAuth redirect.

Common situations: Calling runtime operations (listTools/callTool) against a server that answers 401 and triggers an inline OAuth start without a signed id; calling connect() without authorizationId (see the connect-time guard) via a path that skips it; constructing the provider manually with the wrong flow kind.

Related errors


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