Remote MCP Device Authentication and OAuth Flow in DesktopCommanderMCP

DesktopCommanderMCP authenticates remote devices using the OAuth 2.0 Device Authorization Grant with PKCE, generating device codes for user approval before polling for access tokens that enable secure WebSocket command channels.

DesktopCommanderMCP enables cross-machine control through its Remote MCP (Multi-Computer Protocol) feature. The system implements a secure device authentication flow using OAuth 2.0 standards to verify remote machine identity before establishing command channels. This process, defined in src/remote-device/device-authenticator.ts, creates an authenticated session that subsequent remote operations use to authorize requests.

The OAuth 2.0 Device Authorization Flow

The Remote MCP feature implements the OAuth 2.0 Device Authorization Grant (device-code flow) with PKCE (Proof Key for Code Exchange) to securely link client machines without requiring direct user credentials on the remote device.

PKCE Generation and Security

When initiating a remote session, the client generates cryptographically secure PKCE parameters using the generatePKCE function. This creates a random code verifier and its SHA-256 code challenge, preventing authorization code interception attacks during the device flow.

Requesting the Device Code

The client sends a POST /device/start request to the MCP server with:

  • client_id: Set to mcp-device
  • code_challenge: The SHA-256 PKCE challenge
  • scope: Requested permissions (specifically mcp:tools)
  • Device metadata including hostname, type, and ID

The server responds with a device code (for polling), a user code (for display), and a verification_uri where the user completes authorization.

User Authorization and Token Polling

The Authorization Interface

The client automatically opens the complete verification URL using open(deviceAuth.verification_uri_complete) and displays the short user code. The user must visit this URL in any browser, sign in if necessary, and approve the specific device requesting access.

The Polling Mechanism

While awaiting user approval, the client executes a polling loop via POST /device/poll, sending the device code, client ID, and original code verifier. According to the implementation in src/remote-device/device-authenticator.ts, the server may return several states:

  • authorization_pending: Continue polling at the specified interval
  • slow_down: Increase polling intervals to reduce server load
  • access_token: Authentication succeeded, returning the token plus optional refresh_token and device_id

If the polling exceeds the expiration time or encounters unrecoverable errors, the client logs the failure via captureRemote and throws an authorization error.

Establishing the Remote Channel

Once the OAuth flow completes successfully, the DeviceAuthenticator returns an AuthSession object containing the access_token. This session passes to src/remote-device/remote-channel.ts, which constructs a persistent communication channel using WebSocket or Server-Sent Events (SSE).

The RemoteChannel class includes the Authorization: Bearer <access_token> header in all requests, enabling the server to:

  • Validate the device identity against the issued token
  • Enforce scope-based permissions (mcp:tools)
  • Maintain secure bidirectional communication for commands, file previews, and tool output

Code Implementation Examples

Starting the Authentication Flow

import { DeviceAuthenticator } from './remote-device/device-authenticator';
import { RemoteChannel } from './remote-device/remote-channel';

const authenticator = new DeviceAuthenticator('https://mcp.example.com');

authenticator.authenticate()
  .then(session => {
    console.log('Authenticated:', session.access_token);
    const channel = new RemoteChannel(session.access_token);
    return channel.connect();
  })
  .then(() => console.log('Remote channel ready'))
  .catch(err => console.error('Authentication failed:', err));

Polling Implementation Details

The pollForAuthorization method in device-authenticator.ts handles the retry logic:

private async pollForAuthorization(
  deviceAuth: DeviceAuthResponse,
  codeVerifier: string,
): Promise<AuthSession> {
  const interval = (deviceAuth.interval || 5) * 1000;
  const maxAttempts = Math.floor(deviceAuth.expires_in / (deviceAuth.interval || 5));
  
  for (let attempt = 0; attempt < maxAttempts; ++attempt) {
    await this.sleep(interval);
    
    const resp = await fetch(`${this.baseServerUrl}/device/poll`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        device_code: deviceAuth.device_code,
        client_id: CLIENT_ID,
        code_verifier: codeVerifier,
      }),
    });
    
    const data: PollResponse = await resp.json();
    if (resp.ok && data.access_token) {
      return { 
        access_token: data.access_token, 
        refresh_token: data.refresh_token ?? null, 
        device_id: data.device_id 
      };
    }
    if (data.error === 'authorization_pending') continue;
    if (data.error === 'slow_down') await this.sleep(interval);
  }
  throw new Error('Authorization timeout');
}

Key Implementation Files

The Remote MCP authentication system spans several critical files in the DesktopCommanderMCP repository:

Summary

DesktopCommanderMCP secures remote machine access through a standards-compliant OAuth 2.0 implementation:

  • PKCE-enhanced security: SHA-256 code challenges prevent authorization code interception during the device flow
  • Polling-based activation: The client polls POST /device/poll until receiving an access_token or encountering a timeout
  • Scoped permissions: Tokens enforce mcp:tools scope restrictions on remote operations
  • Bearer token transport: The RemoteChannel includes Authorization: Bearer headers to maintain session state across WebSocket connections
  • Error resilience: Unrecoverable authentication failures trigger captureRemote logging and clear error propagation

Frequently Asked Questions

What OAuth grant type does DesktopCommanderMCP use for remote devices?

DesktopCommanderMCP implements the OAuth 2.0 Device Authorization Grant (RFC 8628), often called the device-code flow. This grant type is designed for input-constrained devices like remote servers that cannot easily accept user credentials directly, combined with PKCE (RFC 7636) for additional security against code interception attacks.

How long does the device authorization polling continue?

The polling duration depends on the expires_in value returned by the POST /device/start endpoint. The pollForAuthorization method calculates maxAttempts by dividing expires_in by the polling interval (defaulting to 5 seconds). If the user does not authorize the device within this window, the client throws an "Authorization timeout" error.

What happens to the access token after authentication completes?

The access_token is stored in an AuthSession object returned by the DeviceAuthenticator. This session is then passed to the RemoteChannel constructor, which includes the token in the Authorization: Bearer header for all subsequent WebSocket or SSE connections. The token enables the MCP server to identify the specific remote device and enforce scope-based access controls on tool executions.

Is the user code displayed securely in DesktopCommanderMCP?

Yes. The client displays the user code locally in the terminal or UI while simultaneously attempting to open the verification_uri_complete URL automatically via the open system command. This two-factor approach ensures the user code is both visible for manual entry and pre-populated in the browser, reducing phishing risks while maintaining usability.

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 →