# MCP OAuth Authorization Flow in OpenWork: Discovery, Callback Handling, and Token Exchange

> Explore OpenWork's MCP OAuth authorization flow. Learn about secure code exchange and callback handling using EnterpriseMcpOAuthProvider. Get your tokens efficiently.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-22

---

**OpenWork's MCP OAuth authorization flow implements a strict OAuth 2.0 Authorization Code grant with PKCE, requiring exact redirect URI matching and state validation through the `EnterpriseMcpOAuthProvider` class to securely exchange codes for tokens.**

The `different-ai/openwork` repository provides a specialized Model-Context-Protocol (MCP) OAuth implementation designed for enterprise security workloads. This system wraps standard OAuth 2.0 primitives in type-safe TypeScript classes that enforce strict discovery binding, exact-match callback validation, and automated token persistence.

## The Three-Stage MCP OAuth Flow

OpenWork structures the authorization process into three distinct stages, each implemented across specific provider and handler modules.

### Stage 1: Discovery and Client Registration

The flow begins with the `EnterpriseMcpOAuthProvider` class loading the authorization server's discovery document. In [`dev/packages/enterprise-mcp-client/src/oauth-provider.ts`](https://github.com/different-ai/openwork/blob/main/dev/packages/enterprise-mcp-client/src/oauth-provider.ts), the `discoveryState()` method fetches the OpenID Connect configuration to locate endpoints dynamically.

If the provider advertises dynamic registration capabilities and the tenant supplies a `clientMetadataUrl`, OpenWork auto-registers the client. Otherwise, administrators must pre-register the client. The [`oauth-discovery-binding.ts`](https://github.com/different-ai/openwork/blob/main/oauth-discovery-binding.ts) module verifies that the discovered issuer matches the administrator-selected authorization server before proceeding, preventing issuer confusion attacks.

### Stage 2: Authorization Request Construction

Once discovery completes, the provider constructs the authorization URL. The `authorizeUrl` property (populated in [`oauth-provider.ts`](https://github.com/different-ai/openwork/blob/main/oauth-provider.ts)) builds a URL containing:

- `client_id` from registered client metadata
- `redirect_uri` supplied during provider instantiation
- `code_challenge` generated via PKCE
- `state` nonce for CSRF protection

This URL directs the user agent to the authorization server. The OpenWork implementation specifically requires that the `redirectUri` provided to the `EnterpriseMcpOAuthProvider` constructor matches exactly what was registered with the identity provider.

### Stage 3: Callback Validation and Token Exchange

After user authorization, the server redirects to the exact `redirect_uri` with `code` and `state` parameters. The callback handling implements strict validation logic in [`dev/packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/dev/packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts):

```typescript
if (callbackWithoutResponse.href !== redirectUri) {
  throw new ProbeFailure(
    "AUTH_USER_OR_WORKLOAD",
    "oauth_authorization",
    "Authorization callback did not exactly match the registered redirect URI"
  );
}

```

The client-side provider validates that the returned `state` matches the original nonce, then exchanges the authorization code for tokens. The token request (sent to the discovered `token_endpoint`) includes the PKCE verifier, client credentials, and the original redirect URI. The server rejects any request where the `redirect_uri` in the POST body differs from the authorization request.

## Strict Callback Handling and Security

OpenWork deliberately enforces exact-match redirect URI validation to prevent open-redirect vulnerabilities. Unlike implementations that allow wildcard or partial matches, the [`oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/oauth-handler.ts) validation requires byte-for-byte equality between the registered URI and the callback URL.

When validation fails, the system throws an `EnterpriseMcpOAuthContractError` with the specific code `MCP_OAUTH_AUTHORIZATION_CALLBACK_FAILED`, defined in [`dev/packages/enterprise-mcp-client/src/errors.ts`](https://github.com/different-ai/openwork/blob/main/dev/packages/enterprise-mcp-client/src/errors.ts). This error indicates potential CSRF attacks, configuration mismatches, or malicious redirect attempts.

The provider also stores the `redirectUri` at construction time and later asserts that the token request's redirect parameter matches exactly, preventing authorization code interception attacks.

## Code Examples

### Initializing the OAuth Provider

```typescript
import { EnterpriseMcpOAuthProvider } from "@different-ai/openwork/packages/enterprise-mcp-client/src/oauth-provider";

const provider = new EnterpriseMcpOAuthProvider({
  redirectUri: "https://myapp.example.com/mcp/callback",
  connectionId: "conn-123",
  persistence: myPersistenceAdapter,
  flow: { kind: "connect", authorizationId: "signed-state-abc" },
  clientName: "My OpenWork Integration",
  clock: myClock,
  lifecycle: myLifecycle,
  authorizationTransactionTtlMs: 5 * 60_000,
  expirationSkewMs: 30_000,
  oauthConfiguration: {
    requestedScopes: ["read", "write"],
  },
});

await provider.discoveryState();
const authUrl = provider.authorizeUrl;

```

### Validating and Processing the Callback

```typescript
const callbackUrl = new URL(request.url);

// Validate state parameter
if (callbackUrl.searchParams.get("state") !== provider.state()) {
  throw new Error("Invalid OAuth state – possible CSRF");
}

// Exchange code for tokens
const tokenResponse = await fetch(provider.clientMetadata.token_endpoint, {
  method: "POST",
  headers: { "Content-Type": "application/x-www-form-urlencoded" },
  body: new URLSearchParams({
    grant_type: "authorization_code",
    code: callbackUrl.searchParams.get("code") ?? "",
    redirect_uri: provider.redirectUrl,
    client_id: provider.clientMetadata.client_id,
    code_verifier: pkceVerifier,
  }),
});

const tokens = await tokenResponse.json();
await provider.persistence.tokens.save(tokens);

```

### Refreshing Access Tokens

```typescript
const refreshed = await provider.refreshAccessToken();
// Automatically handles refresh_token grant and PKCE verification

```

## Summary

- **Discovery binding** in [`oauth-discovery-binding.ts`](https://github.com/different-ai/openwork/blob/main/oauth-discovery-binding.ts) validates the authorization server identity before initiating flows.
- **Exact-match enforcement** prevents open-redirect attacks by requiring the callback URL to match the registered `redirectUri` byte-for-byte.
- **PKCE implementation** protects against authorization code interception during the exchange phase.
- **Typed error handling** through `EnterpriseMcpOAuthContractError` provides specific codes like `MCP_OAUTH_AUTHORIZATION_CALLBACK_FAILED` for debugging and monitoring.
- **Token persistence** is handled automatically by the provider's configured persistence adapter, including expiration tracking and refresh logic.

## Frequently Asked Questions

### What is the MCP OAuth authorization flow?

The MCP OAuth authorization flow is OpenWork's implementation of the OAuth 2.0 Authorization Code grant specifically designed for Model-Context-Protocol connections. It follows a three-stage process—discovery, authorization, and token exchange—while enforcing strict security constraints like exact redirect URI matching and PKCE verification according to the implementation in [`oauth-provider.ts`](https://github.com/different-ai/openwork/blob/main/oauth-provider.ts).

### How does OpenWork prevent open-redirect attacks during OAuth callbacks?

OpenWork prevents open-redirect attacks by requiring exact string matching between the registered `redirectUri` and the incoming callback URL. The [`oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/oauth-handler.ts) validation logic throws a `ProbeFailure` if `callbackWithoutResponse.href !== redirectUri`, rejecting any wildcard patterns, query string variations, or path mutations that attackers might exploit to steal authorization codes.

### What happens if the redirect URI doesn't match exactly?

If the redirect URI doesn't match exactly, the server returns an OAuth error such as `invalid_grant` or `invalid_request`, and the client-side `EnterpriseMcpOAuthProvider` throws an `EnterpriseMcpOAuthContractError` with code `MCP_OAUTH_AUTHORIZATION_CALLBACK_FAILED`. This strict validation occurs both in the callback handler and during the token exchange POST request.

### Where is the token exchange logic implemented?

The token exchange logic is implemented in [`dev/packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/dev/packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts) for the server-side validation and grant processing, while the client-side initiation and persistence logic resides in [`dev/packages/enterprise-mcp-client/src/oauth-provider.ts`](https://github.com/different-ai/openwork/blob/main/dev/packages/enterprise-mcp-client/src/oauth-provider.ts). The server verifies the PKCE code verifier, client authentication, and redirect URI matching before issuing access tokens.