fix(oauth): harden browser launch handling

This commit is contained in:
Armin Ronacher
2026-06-02 23:15:46 +02:00
parent a98e087e5d
commit ba6e5298df
6 changed files with 138 additions and 17 deletions

View File

@@ -132,10 +132,22 @@ async function startDeviceFlow(domain: string): Promise<DeviceCodeResponse> {
throw new Error("Invalid device code response fields");
}
// The verification URI is opened in the user's browser and to prevent `open` from
// opening an executable or similar, we force it to be a URL.
let parsedUri: URL;
try {
parsedUri = new URL(verificationUri);
} catch {
throw new Error("Untrusted verification_uri in device code response");
}
if (parsedUri.protocol !== "https:" && parsedUri.protocol !== "http:") {
throw new Error("Untrusted verification_uri in device code response");
}
return {
device_code: deviceCode,
user_code: userCode,
verification_uri: verificationUri,
verification_uri: parsedUri.href,
interval,
expires_in: expiresIn,
};

View File

@@ -28,7 +28,6 @@ import type {
OAuthProviderInterface,
} from "./types.ts";
const CALLBACK_HOST = process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1";
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
const AUTH_BASE_URL = "https://auth.openai.com";
const AUTHORIZE_URL = `${AUTH_BASE_URL}/oauth/authorize`;
@@ -47,6 +46,10 @@ const JWT_CLAIM_PATH = "https://api.openai.com/auth";
type OAuthToken = { access: string; refresh: string; expires: number };
type TokenOperation = "exchange" | "refresh";
function getCallbackHost(): string {
return typeof process !== "undefined" ? process.env.PI_OAUTH_CALLBACK_HOST || "127.0.0.1" : "127.0.0.1";
}
type DeviceAuthInfo = {
deviceAuthId: string;
userCode: string;
@@ -368,7 +371,7 @@ function startLocalOAuthServer(state: string): Promise<OAuthServerInfo> {
return new Promise((resolve) => {
server
.listen(1455, CALLBACK_HOST, () => {
.listen(1455, getCallbackHost(), () => {
resolve({
close: () => server.close(),
cancelWait: () => {