Skip to content

Commit e1a6b71

Browse files
Theodor N. EngøyTheodor N. Engøy
authored andcommitted
examples: avoid shell exec when opening browser
1 parent f96e509 commit e1a6b71

2 files changed

Lines changed: 40 additions & 14 deletions

File tree

examples/client/src/elicitationUrlExample.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// URL elicitation allows servers to prompt the end-user to open a URL in their browser
66
// to collect sensitive information.
77

8-
import { exec } from 'node:child_process';
8+
import { spawn } from 'node:child_process';
99
import { createServer } from 'node:http';
1010
import { createInterface } from 'node:readline';
1111

@@ -274,14 +274,27 @@ async function elicitationLoop(): Promise<void> {
274274
}
275275

276276
async function openBrowser(url: string): Promise<void> {
277-
const command = `open "${url}"`;
277+
const platform = process.platform;
278+
let cmd: string;
279+
let args: string[];
280+
281+
if (platform === 'darwin') {
282+
cmd = 'open';
283+
args = [url];
284+
} else if (platform === 'win32') {
285+
cmd = 'cmd';
286+
args = ['/c', 'start', '', url];
287+
} else {
288+
cmd = 'xdg-open';
289+
args = [url];
290+
}
278291

279-
exec(command, error => {
280-
if (error) {
281-
console.error(`Failed to open browser: ${error.message}`);
282-
console.log(`Please manually open: ${url}`);
283-
}
292+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
293+
child.on('error', error => {
294+
console.error(`Failed to open browser: ${error.message}`);
295+
console.log(`Please manually open: ${url}`);
284296
});
297+
child.unref();
285298
}
286299

287300
/**

examples/client/src/simpleOAuthClient.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env node
22

3-
import { exec } from 'node:child_process';
3+
import { spawn } from 'node:child_process';
44
import { createServer } from 'node:http';
55
import { createInterface } from 'node:readline';
66
import { URL } from 'node:url';
@@ -53,14 +53,27 @@ class InteractiveOAuthClient {
5353
private async openBrowser(url: string): Promise<void> {
5454
console.log(`🌐 Opening browser for authorization: ${url}`);
5555

56-
const command = `open "${url}"`;
56+
const platform = process.platform;
57+
let cmd: string;
58+
let args: string[];
59+
60+
if (platform === 'darwin') {
61+
cmd = 'open';
62+
args = [url];
63+
} else if (platform === 'win32') {
64+
cmd = 'cmd';
65+
args = ['/c', 'start', '', url];
66+
} else {
67+
cmd = 'xdg-open';
68+
args = [url];
69+
}
5770

58-
exec(command, error => {
59-
if (error) {
60-
console.error(`Failed to open browser: ${error.message}`);
61-
console.log(`Please manually open: ${url}`);
62-
}
71+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true });
72+
child.on('error', error => {
73+
console.error(`Failed to open browser: ${error.message}`);
74+
console.log(`Please manually open: ${url}`);
6375
});
76+
child.unref();
6477
}
6578
/**
6679
* Example OAuth callback handler - in production, use a more robust approach

0 commit comments

Comments
 (0)