Remote MCP Device Authentication and OAuth Flow: Complete Implementation Guide
Remote MCP uses the OAuth 2.0 Device Authorization Grant to authenticate headless devices through a secure polling mechanism where users authorize access via a separate browser session.
The wonderwhy-er/DesktopCommanderMCP repository implements a robust authentication system for remote devices using the OAuth 2.0 Device Authorization Grant. This Remote MCP device authentication flow enables secure access for headless or input-constrained devices by delegating user interaction to a secondary browser session. According to the source documentation in src/remote-device/README.md, the implementation follows the official OAuth 2.0 specification to ensure strong security guarantees while maintaining a simple user experience.
How the OAuth 2.0 Device Flow Works in Remote MCP
The Remote MCP device authentication process follows the standard OAuth 2.0 Device Authorization Grant through five distinct phases. Each phase ensures that devices without browsers or input capabilities can securely obtain access tokens.
- Device initiates login – The client sends a
POST /device/coderequest to the server's device-code endpoint. The response includes a device code, user code, and verification URL. - User authorizes – The user opens the verification URL on any browser, enters the short-lived user code, and signs in with their account.
- Device polls for token – While the user authorizes, the client repeatedly polls
POST /tokenwith the device code at the specified interval (typically every 5 seconds). Once authorization completes, the server returns an access token. - Secure channel establishment – The client stores the access token locally in encrypted storage for subsequent API calls.
- Token renewal – If provided, the client uses a refresh token to silently obtain new access tokens before expiration.
Client-Side Implementation: Requesting Device Codes
In src/auth/deviceAuth.ts, the client-side logic handles the initial device code request and token polling. The startDeviceAuth() function initiates the flow, while pollForToken() manages the polling loop.
Initiating the Authentication Flow
The following TypeScript implementation demonstrates how the Remote MCP client requests a device code and begins the polling process:
import { startDeviceAuth } from './auth/deviceAuth';
async function initRemoteMcp() {
// Trigger the device‑code request
const { device_code, user_code, verification_uri, expires_in, interval } =
await startDeviceAuth();
console.log(`Visit ${verification_uri} and enter code: ${user_code}`);
// Poll for the token
const token = await pollForToken(device_code, interval);
// Store token securely (e.g., encrypted local storage)
await saveToken(token);
}
Server-Side Implementation: Handling Device Codes and Tokens
The server implementation in src/server/routes/device.ts manages the device code lifecycle and token issuance. This includes generating unique codes, validating user authorization status, and issuing bearer tokens.
Generating Device and User Codes
When a device initiates authentication, the server generates a UUID-based device code and a human-readable user code. The deviceCodeHandler function in src/server/routes/device.ts persists these mappings with expiration timestamps:
import { v4 as uuidv4 } from 'uuid';
import { addDeviceCode } from './store';
export async function deviceCodeHandler(req, res) {
const deviceCode = uuidv4();
const userCode = generateHumanReadableCode(); // e.g., “ABCD‑EFGH”
const verificationUri = `${process.env.BASE_URL}/device`;
// Persist the mapping with an expiration timestamp
await addDeviceCode({
deviceCode,
userCode,
expiresAt: Date.now() + 10 * 60 * 1000, // 10 min
});
res.json({
device_code: deviceCode,
user_code: userCode,
verification_uri: verificationUri,
expires_in: 600,
interval: 5,
});
}
Processing Token Polling Requests
The tokenHandler function validates device codes and returns access tokens once the user completes authorization. Stored in src/server/routes/device.ts, this endpoint handles the polling requests from waiting devices:
import { getDeviceCodeEntry, generateAccessToken } from './store';
export async function tokenHandler(req, res) {
const { device_code } = req.body;
const entry = await getDeviceCodeEntry(device_code);
if (!entry) return res.status(400).json({ error: 'invalid_device_code' });
if (Date.now() > entry.expiresAt) return res.status(400).json({ error: 'expired_token' });
if (!entry.authorized) return res.status(428).json({ error: 'authorization_pending' });
const accessToken = generateAccessToken(entry.userId);
res.json({ access_token: accessToken, token_type: 'Bearer', expires_in: 3600 });
}
Secure Token Storage and Session Management
After obtaining an access token, the Remote MCP client stores credentials securely using encrypted local storage. The saveToken() function ensures that bearer tokens remain accessible for authenticated API calls while protecting against local extraction attacks. If the server issues a refresh token during the tokenHandler exchange, the client can silently renew sessions without requiring repeated user authorization.
Key Files in the DesktopCommanderMCP Repository
The following source files define the end-to-end authentication mechanism in the wonderwhy-er/DesktopCommanderMCP repository:
src/remote-device/README.md– Overview of the remote device feature and OAuth flow descriptionsrc/auth/deviceAuth.ts– Client-side helpers that request device codes and poll for access tokenssrc/server/routes/device.ts– Server-side endpoints that issue device codes and exchange them for tokenssrc/server/store.ts– Persistence layer for device-code mappings and token storagepackage.json– Lists OAuth-related dependencies such asaxiosandjsonwebtoken
Summary
Remote MCP device authentication implements the OAuth 2.0 Device Authorization Grant to securely connect headless devices to the DesktopCommanderMCP service. Key implementation details include:
- The device flow separates user interaction from device authentication by using short-lived user codes and verification URLs
startDeviceAuth()insrc/auth/deviceAuth.tsinitiates the flow whilepollForToken()handles the polling loop- Server endpoints in
src/server/routes/device.tsmanage code generation, validation, and token issuance - Tokens are stored in encrypted local storage with support for silent renewal via refresh tokens
- The 10-minute expiration window for device codes ensures security while allowing adequate time for user authorization
Frequently Asked Questions
What is the OAuth 2.0 Device Authorization Grant?
The OAuth 2.0 Device Authorization Grant is a protocol designed for devices that lack browsers or input methods, such as IoT devices or CLI tools. It allows these devices to obtain access tokens by having users complete authorization on a separate device with a browser, such as a smartphone or laptop.
How long does the device code remain valid in Remote MCP?
According to the implementation in src/server/routes/device.ts, device codes expire after 10 minutes (600 seconds). The expiresAt timestamp is set to Date.now() + 10 * 60 * 1000 when the code is generated, providing users with sufficient time to complete authentication.
What happens if the user doesn't authorize the device before expiration?
If the user fails to authorize the device before the 10-minute window closes, the tokenHandler returns an expired_token error with HTTP status 400. The device must then restart the authentication flow by requesting a new device code through startDeviceAuth().
Can Remote MCP refresh access tokens without user interaction?
Yes, if the initial token exchange provides a refresh token, the Remote MCP client can silently obtain new access tokens before the current one expires. This keeps the remote device online indefinitely without requiring repeated user authorization, as implemented in the token storage logic of src/auth/deviceAuth.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →