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 tomcp-devicecode_challenge: The SHA-256 PKCE challengescope: Requested permissions (specificallymcp: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 intervalslow_down: Increase polling intervals to reduce server loadaccess_token: Authentication succeeded, returning the token plus optionalrefresh_tokenanddevice_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:
src/remote-device/device-authenticator.ts: Implements the complete OAuth 2.0 device-code flow with PKCE and polling logicsrc/remote-device/remote-channel.ts: Manages the authenticated WebSocket/SSE connection using Bearer token authorizationsrc/remote-device/device.ts: Represents the remote device object and stores authenticated session statesrc/remote-device/desktop-commander-integration.ts: Bridges the UI with the remote-device authentication flowsrc/utils/capture.ts: Provides telemetry logging for authentication events viacaptureRemote
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/polluntil receiving anaccess_tokenor encountering a timeout - Scoped permissions: Tokens enforce
mcp:toolsscope restrictions on remote operations - Bearer token transport: The
RemoteChannelincludesAuthorization: Bearerheaders to maintain session state across WebSocket connections - Error resilience: Unrecoverable authentication failures trigger
captureRemotelogging 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →