# OpenWork MCP OAuth Security Considerations: Enterprise-Grade Protection in a Mock Server

> Secure your OpenWork MCP OAuth with enterprise-grade protection. Discover strict URI validation, PKCE enforcement, dynamic registration, and token management for robust security.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-06

---

**OpenWork's MCP OAuth implementation mitigates common attack vectors through strict redirect-URI validation, PKCE enforcement, dynamic client registration, and comprehensive token lifecycle management.**

The `different-ai/openwork` repository provides a Model-Context-Protocol (MCP) mock server that implements a synthetic OAuth 2.0 provider for deterministic testing. Understanding the OpenWork MCP OAuth security considerations is essential for developers integrating enterprise connectors, as the implementation balances realistic flow simulation with defensive checks against open redirects, credential leakage, and token replay attacks.

## Redirect-URI Validation and Open Redirect Prevention

The implementation strictly validates redirect URIs to prevent open-redirect attacks. In [`packages/enterprise-mcp-mock-server/src/contracts/oauth.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-mock-server/src/contracts/oauth.ts), the `isSafeOAuthRedirectUri` utility enforces that URIs contain no user-info, no fragment, and use HTTPS unless the host is localhost (`localhost`, `127.0.0.1`, or `[::1]`).

This check ensures that authorization codes cannot be diverted to attacker-controlled endpoints during the flow.

```typescript
import { isSafeOAuthRedirectUri } from '@modelcontextprotocol/sdk/shared/auth.js';

if (!isSafeOAuthRedirectUri(redirectUri)) {
  throw new Error('Unsafe redirect URI');
}

```

## Dynamic Client Registration Requirements

The handler enforces dynamic client registration (DCR) when scenarios require it. Located in [`packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/packages/enterprise-mcp-mock-server/src/protocol/oauth-handler.ts) (lines 151-161), the code rejects manual OAuth authorization requests when the `dcr-required` fault is active.

This ensures clients are properly registered and validated before any authorization request proceeds, preventing unauthorized client impersonation.

## PKCE Enforcement Against Code Interception

To mitigate authorization code interception attacks, the implementation enforces **PKCE** (Proof Key for Code Exchange) with S256 method validation. The handler validates `code_challenge` against `pkceS256ChallengePattern` and later verifies the verifier using `pkceVerifierPattern` with a timing-safe equality check (lines 120-127 in [`oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/oauth-handler.ts)).

```typescript
const verifier = form.get('code_verifier')!;
if (!pkceVerifierPattern.test(verifier) ||
    !safeEqual(storedCodeChallenge, pkceChallenge(verifier))) {
  sendOAuthError(res, 400, 'invalid_grant', 'PKCE verification failed');
}

```

## Client Secret Verification and Credential Protection

The `sendInvalidClient` function (lines 69-82 in [`oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/oauth-handler.ts)) handles client authentication failures with provider-specific error formatting for Microsoft and ServiceNow. The system maintains strict secret length requirements enforced by the in-memory state store in [`src/runtime/instance-state.ts`](https://github.com/different-ai/openwork/blob/main/src/runtime/instance-state.ts), protecting against credential leakage through brute-force or guessing attacks.

## Scope Validation and Privilege Limitation

Scope validation prevents over-privilege by rejecting requests where the `scope` parameter is empty or includes any scope not listed in `scenario.oauth.authorizationScopes` (lines 172-179). This ensures tokens are issued only with explicitly defined permissions, adhering to the principle of least privilege.

## Token Lifecycle Management and Revocation

Token issuance follows strict lifetime policies: **access tokens expire after 1 hour** and **refresh tokens expire after 30 days**. The `issueTokenResponse` function (lines 389-418) creates opaque tokens and records expiration metadata.

For security events, `revokeTokenFamily` (lines 430-438) performs cascade deletion of all access and refresh tokens sharing the same `familyId`, preventing token reuse after compromise.

```typescript
function revokeTokenFamily(state, familyId) {
  for (const [token, rec] of state.tokens) {
    if (rec.familyId === familyId) state.tokens.delete(token);
  }
  for (const [token, rec] of state.refreshTokens) {
    if (rec.familyId === familyId) state.refreshTokens.delete(token);
  }
}

```

## Fault Injection for Controlled Testing

The implementation includes `faultApplies` and `emitFault` hooks (lines 38-46) that enable controlled fault simulation without disabling underlying validation logic. This allows developers to test error scenarios—such as invalid grants or server errors—while maintaining the integrity of security boundaries.

## Summary

- **Redirect-URI validation** in [`oauth.ts`](https://github.com/different-ai/openwork/blob/main/oauth.ts) blocks open-redirect attacks by enforcing HTTPS and localhost safety checks.
- **Dynamic client registration** requirements prevent unauthorized clients from initiating flows in [`oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/oauth-handler.ts).
- **PKCE enforcement** using `pkceS256ChallengePattern` and timing-safe comparison mitigates code interception.
- **Client secret verification** includes provider-specific error handling and minimum length requirements.
- **Scope validation** ensures tokens are issued only with explicitly permitted scopes defined in the active scenario.
- **Token lifecycle management** provides 1-hour access token and 30-day refresh token expiration with family-based revocation.
- **Fault injection hooks** allow realistic error testing while preserving security control flow.

## Frequently Asked Questions

### How does OpenWork prevent open-redirect vulnerabilities in OAuth flows?

OpenWork prevents open-redirect attacks through the `isSafeOAuthRedirectUri` utility in [`src/contracts/oauth.ts`](https://github.com/different-ai/openwork/blob/main/src/contracts/oauth.ts), which validates that redirect URIs contain no user-info, no fragment, and use HTTPS unless pointing to localhost (`localhost`, `127.0.0.1`, or `[::1]`). This ensures authorization codes cannot be diverted to malicious endpoints during the authentication flow.

### What PKCE validation does the OpenWork MCP server enforce?

The server enforces PKCE S256 method validation using regex patterns `pkceS256ChallengePattern` and `pkceVerifierPattern` in [`src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/src/protocol/oauth-handler.ts). During token exchange, it performs a timing-safe equality check between the stored code challenge and the hash of the provided verifier, preventing authorization code interception attacks.

### How are refresh tokens revoked in case of compromise?

The `revokeTokenFamily` function in [`src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/src/protocol/oauth-handler.ts) (lines 430-438) implements cascade revocation by iterating through token maps and deleting all access and refresh tokens sharing the same `familyId`. This ensures that compromising one token invalidates the entire token family, preventing replay attacks.

### Does the mock server support dynamic client registration?

Yes, the implementation supports dynamic client registration (DCR) through endpoints defined in [`src/protocol/oauth-handler.ts`](https://github.com/different-ai/openwork/blob/main/src/protocol/oauth-handler.ts). When scenarios require DCR (via the `dcr-required` fault), the server rejects manual authorization attempts and requires clients to register via the `/oauth/registration` endpoint, issuing opaque client IDs and secrets through `state.issueOpaque()`.