How OpenWork Manages Provider Credentials and Handles Credentials Sync

OpenWork manages provider credentials through a decentralized architecture that stores OAuth tokens locally in ~/.config/openwork/credentials.json while synchronizing state with the Den control plane via Model-Control-Protocol (MCP), supporting both shared and per-member credential modes with automatic refresh handling and secure redaction.

OpenWork's architecture isolates sensitive provider credentials from application logic to ensure security across distributed environments. The system implements a deterministic sync protocol that coordinates credential state between the desktop client, the Den control plane, and remote MCP connections. This approach ensures that API keys and OAuth tokens remain encrypted, versioned, and automatically refreshed without exposing secrets to logs or diagnostic streams.

Credential Storage Architecture

OpenWork distributes credential storage across three distinct layers, each optimized for its specific security and accessibility requirements.

Desktop Client Storage

The desktop client persists OAuth access tokens, refresh tokens, and API keys for providers like Anthropic and OpenAI in ~/.config/openwork/credentials.json. In development environments, the client uses a mock keychain instead of the system credential store. All credential objects conform to the EnterpriseMcpOAuthCredential interface defined in packages/enterprise-mcp-client/src/client.ts, ensuring type safety through Zod schema validation before any data traverses the network.

Den Control Plane Storage

The Den control plane maintains centralized, encrypted records for organization members and teams. These CredentialRecord objects enable cross-team synchronization while preserving access controls. The server manages credential continuity through the credentialContinuity mechanism implemented in packages/enterprise-mcp-mock-server/src/runtime/mock-server.ts, which preserves OAuth refresh tokens across client activations and prevents unnecessary re-authorization flows.

MCP Remote Handling

Remote MCP connections transmit credential metadata (such as bearer tokens) over the transport layer without persisting them on the client. The CredentialMode enum—defined in packages/types/src/agent-context-diagnostics.ts—determines whether credentials travel as shared resources or per_member assignments, dictating how the sync protocol handles token refresh and invalidation.

Credential Modes: Shared vs. Per-Member

OpenWork distinguishes two modes of credential handling through the credentialMode field declared in packages/types/src/den/mcp-connection-action.ts.

Shared mode allows a single credential to be reused by every member of a team. This model suits organization-wide API keys or service accounts where individual identity is irrelevant to the provider.

Per-member mode assigns each user a distinct credential that may be refreshed independently. This approach is essential when providers enforce individual rate limits or when audit trails require user-specific access tokens.

The mode declaration (credentialMode: z.enum(["shared","per_member"])) drives how the desktop client reads, updates, and syncs credentials during the MCP lifecycle.

The Credentials Sync Lifecycle

The credentials sync process follows a deterministic five-phase cycle that maintains consistency across distributed components.

Initialization

When the desktop app launches, it reads persisted credentials from the local keychain or generates temporary mock credentials for development. The client advertises its credential mode and current revision to the MCP via the engine-mcp-sync diagnostic event, using the syncStatus enum defined in packages/types/src/agent-context-diagnostics.ts.

MCP Sync Call

The client issues an mcp-sync RPC to the Den server with a payload containing the current credential revision hash and the declared credentialMode. This request enables the server to detect stale or outdated tokens without transmitting the actual secret values.

Server-Side Reconciliation

Den validates the incoming revision against its encrypted database, merges organization-wide updates, and calculates the authoritative credential state. If the server detects an expired token, it returns an oauth_credential_expired fault, prompting the client to initiate a fresh OAuth flow rather than attempting a silent refresh.

Client Update

Upon successful sync, the client persists refreshed credentials—including new access and refresh tokens—and increments its local revision counter via nextRevision(). The updated syncStatus emits as part of the diagnostic stream, allowing downstream agents to detect credential continuity and retry tool executions that previously failed due to token staleness.

Invalidation and Recovery

When tool execution receives a 401 response with invalid_token, the client records a credential-invalidation event, clears the stored credential from ~/.config/openwork/credentials.json, and forces a re-authorization flow. The enterprise MCP client test suite in packages/enterprise-mcp-client/test/enterprise-mcp-client.test.ts validates this behavior through simulated credential-invalidation events.

Security and Redaction

All outbound diagnostic text passes through a redaction pipeline defined by diagnosticOriginSchema in packages/types/src/agent-context-diagnostics.ts. This pipeline automatically removes raw tokens, URLs, and absolute paths from logs, screenshots, and test tapes. The redaction guarantees that even diagnostic dumps or error reports never expose secrets, adhering to the principle that credentials remain inscrutable outside the encrypted storage boundaries.

Implementation Examples

Loading Credentials from Local Storage

The CredentialStore class in packages/enterprise-mcp-client/src/client.ts abstracts keychain access:

class CredentialStore {
  private credential?: EnterpriseMcpOAuthCredential;

  async load(): Promise<EnterpriseMcpOAuthCredential | undefined> {
    // In production this reads the real keychain; in dev we use a mock.
    return this.credential;
  }

  async save(newCred: EnterpriseMcpOAuthCredential) {
    this.credential = newCred;
    // Persist to disk or keychain as appropriate.
  }
}

Performing an MCP Sync

The sync function constructs the revision payload and handles expiration faults:

async function syncCredentials() {
  const local = await credentialStore.load();
  const payload = {
    credentialMode: "per_member",
    credentialRevision: local?.revision,
  };
  const response = await mcpRpc("engine-mcp-sync", payload);
  if (response.error === "oauth_credential_expired") {
    await startOAuthFlow(); // Re‑authorize the user
  } else {
    await credentialStore.save(response.credential);
  }
}

Handling Token Invalidation During Tool Execution

The error handler wipes stale tokens and triggers re-authentication:

async function runTool() {
  try {
    await mcpRpc("tool-execute", { /* … */ });
  } catch (e) {
    if (e.kind === "credential-invalidation") {
      await credentialStore.save(undefined); // wipe stale token
      await startOAuthFlow();               // force fresh login
    }
  }
}

Summary

  • OpenWork stores provider credentials locally in ~/.config/openwork/credentials.json while synchronizing state with the Den control plane via MCP.
  • Credential modes (shared and per_member) in packages/types/src/den/mcp-connection-action.ts determine multi-user access patterns and refresh strategies.
  • The sync lifecycle uses revision hashes and engine-mcp-sync RPC calls to maintain consistency without transmitting secrets.
  • Automatic redaction via diagnosticOriginSchema ensures tokens never appear in logs or diagnostic outputs.
  • Expired tokens trigger oauth_credential_expired faults, while runtime invalidations emit credential-invalidation events that force re-authorization.

Frequently Asked Questions

Where does OpenWork store provider credentials on the local machine?

OpenWork persists credentials in ~/.config/openwork/credentials.json on the desktop client, utilizing the system keychain in production environments and a mock store during development. This local storage maintains EnterpriseMcpOAuthCredential objects containing access tokens, refresh tokens, and revision counters, while the Den control plane maintains encrypted CredentialRecord entries for organizational synchronization.

What is the difference between shared and per-member credential modes?

Shared mode utilizes a single credential across all team members, suitable for organization-wide API keys, while per-member mode assigns unique credentials to each user, enabling independent token refresh and individual audit trails. The mode is declared in the MCP connection configuration at packages/types/src/den/mcp-connection-action.ts and dictates how the sync protocol reconciles state between the client and Den server.

How does OpenWork handle expired or invalid OAuth tokens?

When the Den server detects an expired token during the mcp-sync RPC, it returns an oauth_credential_expired error that triggers the client to initiate a fresh OAuth flow. During tool execution, a 401 response generates a credential-invalidation event, causing the client to clear the stored credential via credentialStore.save(undefined) and force immediate re-authorization, as validated in packages/enterprise-mcp-client/test/enterprise-mcp-client.test.ts.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →