Skip to content

Commit 782ec3e

Browse files
committed
fix: enhance authentication support for Firebase Studio IDE
- Add Firebase Studio IDE environment detection in URI handler - Enhance WebAuthService with cloud IDE specific handling - Improve user agent detection to include environment info - Add comprehensive logging for authentication debugging - Provide better user feedback for cloud IDE environments Fixes #6369
1 parent b117c0f commit 782ec3e

File tree

4 files changed

+226
-9
lines changed

4 files changed

+226
-9
lines changed

packages/cloud/src/auth/WebAuthService.ts

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,24 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
242242
* This method initiates the authentication flow by generating a state parameter
243243
* and opening the browser to the authorization URL.
244244
*/
245+
/**
246+
* Detects if running in Firebase Studio IDE (formerly IDX Google) environment
247+
*/
248+
private isFirebaseStudioIDE(): boolean {
249+
const appName = vscode.env.appName?.toLowerCase() || ""
250+
const remoteName = vscode.env.remoteName?.toLowerCase() || ""
251+
252+
return (
253+
appName.includes("idx") ||
254+
appName.includes("firebase") ||
255+
appName.includes("studio") ||
256+
remoteName.includes("idx") ||
257+
remoteName.includes("firebase") ||
258+
process.env.IDX_WORKSPACE_ID !== undefined ||
259+
process.env.FIREBASE_PROJECT_ID !== undefined
260+
)
261+
}
262+
245263
public async login(): Promise<void> {
246264
try {
247265
// Generate a cryptographically random state parameter.
@@ -250,11 +268,34 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
250268
const packageJSON = this.context.extension?.packageJSON
251269
const publisher = packageJSON?.publisher ?? "RooVeterinaryInc"
252270
const name = packageJSON?.name ?? "roo-cline"
271+
272+
const isCloudIDE = this.isFirebaseStudioIDE()
273+
this.log(`[auth] Initiating login - Firebase Studio IDE detected: ${isCloudIDE}`)
274+
this.log(`[auth] App name: ${vscode.env.appName}`)
275+
this.log(`[auth] Remote name: ${vscode.env.remoteName}`)
276+
this.log(`[auth] URI scheme: ${vscode.env.uriScheme}`)
277+
253278
const params = new URLSearchParams({
254279
state,
255280
auth_redirect: `${vscode.env.uriScheme}://${publisher}.${name}`,
256281
})
282+
283+
// Add cloud IDE indicator for server-side handling
284+
if (isCloudIDE) {
285+
params.append("cloud_ide", "firebase_studio")
286+
}
287+
257288
const url = `${getRooCodeApiUrl()}/extension/sign-in?${params.toString()}`
289+
this.log(`[auth] Opening authentication URL: ${url}`)
290+
291+
if (isCloudIDE) {
292+
// Show additional guidance for Firebase Studio IDE users
293+
vscode.window.showInformationMessage(
294+
"Opening authentication in Firebase Studio IDE. After signing in, the callback should be automatically handled.",
295+
{ modal: false },
296+
)
297+
}
298+
258299
await vscode.env.openExternal(vscode.Uri.parse(url))
259300
} catch (error) {
260301
this.log(`[auth] Error initiating Roo Code Cloud auth: ${error}`)
@@ -277,8 +318,24 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
277318
state: string | null,
278319
organizationId?: string | null,
279320
): Promise<void> {
321+
const isCloudIDE = this.isFirebaseStudioIDE()
322+
323+
this.log(`[auth] Handling callback - Firebase Studio IDE: ${isCloudIDE}`)
324+
this.log(`[auth] Code present: ${!!code}, State present: ${!!state}`)
325+
280326
if (!code || !state) {
281-
vscode.window.showInformationMessage("Invalid Roo Code Cloud sign in url")
327+
const message = "Invalid Roo Code Cloud sign in url"
328+
this.log(`[auth] ${message}`)
329+
330+
if (isCloudIDE) {
331+
// Provide more specific guidance for Firebase Studio IDE
332+
vscode.window.showErrorMessage(
333+
"Authentication callback failed in Firebase Studio IDE. Please try signing in again. " +
334+
"If the issue persists, check that popup blockers are disabled and the extension has proper permissions.",
335+
)
336+
} else {
337+
vscode.window.showInformationMessage(message)
338+
}
282339
return
283340
}
284341

@@ -288,20 +345,40 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
288345

289346
if (state !== storedState) {
290347
this.log("[auth] State mismatch in callback")
348+
this.log(`[auth] Expected state: ${storedState}, Received state: ${state}`)
291349
throw new Error("Invalid state parameter. Authentication request may have been tampered with.")
292350
}
293351

352+
this.log("[auth] State validation successful, proceeding with sign-in")
294353
const credentials = await this.clerkSignIn(code)
295354

296355
// Set organizationId (null for personal accounts)
297356
credentials.organizationId = organizationId || null
298357

299358
await this.storeCredentials(credentials)
300359

301-
vscode.window.showInformationMessage("Successfully authenticated with Roo Code Cloud")
360+
const successMessage = "Successfully authenticated with Roo Code Cloud"
361+
if (isCloudIDE) {
362+
vscode.window.showInformationMessage(
363+
`${successMessage} in Firebase Studio IDE. You can now use Roo Code Cloud features.`,
364+
)
365+
} else {
366+
vscode.window.showInformationMessage(successMessage)
367+
}
368+
302369
this.log("[auth] Successfully authenticated with Roo Code Cloud")
303370
} catch (error) {
304371
this.log(`[auth] Error handling Roo Code Cloud callback: ${error}`)
372+
373+
if (isCloudIDE) {
374+
// Provide more detailed error information for Firebase Studio IDE
375+
const errorMessage = error instanceof Error ? error.message : String(error)
376+
vscode.window.showErrorMessage(
377+
`Authentication failed in Firebase Studio IDE: ${errorMessage}. ` +
378+
"Please try again or contact support if the issue persists.",
379+
)
380+
}
381+
305382
const previousState = this.state
306383
this.state = "logged-out"
307384
this.emit("logged-out", { previousState })

packages/cloud/src/utils.ts

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,46 @@
11
import * as vscode from "vscode"
22

3+
/**
4+
* Detects if running in Firebase Studio IDE (formerly IDX Google) environment
5+
*/
6+
function isFirebaseStudioIDE(): boolean {
7+
const appName = vscode.env.appName?.toLowerCase() || ""
8+
const remoteName = vscode.env.remoteName?.toLowerCase() || ""
9+
10+
return (
11+
appName.includes("idx") ||
12+
appName.includes("firebase") ||
13+
appName.includes("studio") ||
14+
remoteName.includes("idx") ||
15+
remoteName.includes("firebase") ||
16+
process.env.IDX_WORKSPACE_ID !== undefined ||
17+
process.env.FIREBASE_PROJECT_ID !== undefined
18+
)
19+
}
20+
321
/**
422
* Get the User-Agent string for API requests
523
* @param context Optional extension context for more accurate version detection
6-
* @returns User-Agent string in format "Roo-Code {version}"
24+
* @returns User-Agent string in format "Roo-Code {version} ({environment})"
725
*/
826
export function getUserAgent(context?: vscode.ExtensionContext): string {
9-
return `Roo-Code ${context?.extension?.packageJSON?.version || "unknown"}`
27+
const version = context?.extension?.packageJSON?.version || "unknown"
28+
const baseUserAgent = `Roo-Code ${version}`
29+
30+
// Add environment information for better debugging
31+
const environment = []
32+
33+
if (isFirebaseStudioIDE()) {
34+
environment.push("Firebase-Studio-IDE")
35+
}
36+
37+
if (vscode.env.remoteName) {
38+
environment.push(`Remote-${vscode.env.remoteName}`)
39+
}
40+
41+
if (environment.length > 0) {
42+
return `${baseUserAgent} (${environment.join("; ")})`
43+
}
44+
45+
return baseUserAgent
1046
}

src/activate/handleUri.ts

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,67 @@ import { CloudService } from "@roo-code/cloud"
44

55
import { ClineProvider } from "../core/webview/ClineProvider"
66

7+
/**
8+
* Detects if running in Firebase Studio IDE (formerly IDX Google) environment
9+
*/
10+
function isFirebaseStudioIDE(): boolean {
11+
// Check for Firebase Studio IDE specific environment indicators
12+
const appName = vscode.env.appName?.toLowerCase() || ""
13+
const remoteName = vscode.env.remoteName?.toLowerCase() || ""
14+
15+
// Firebase Studio IDE typically has these characteristics
16+
return (
17+
appName.includes("idx") ||
18+
appName.includes("firebase") ||
19+
appName.includes("studio") ||
20+
remoteName.includes("idx") ||
21+
remoteName.includes("firebase") ||
22+
process.env.IDX_WORKSPACE_ID !== undefined ||
23+
process.env.FIREBASE_PROJECT_ID !== undefined
24+
)
25+
}
26+
727
export const handleUri = async (uri: vscode.Uri) => {
828
const path = uri.path
929
const query = new URLSearchParams(uri.query.replace(/\+/g, "%2B"))
1030
const visibleProvider = ClineProvider.getVisibleInstance()
1131

32+
// Enhanced logging for debugging authentication issues in cloud IDEs
33+
const isCloudIDE = isFirebaseStudioIDE()
34+
console.log(`[handleUri] Processing URI: ${uri.toString()}`)
35+
console.log(`[handleUri] Path: ${path}`)
36+
console.log(`[handleUri] Query params: ${query.toString()}`)
37+
console.log(`[handleUri] Firebase Studio IDE detected: ${isCloudIDE}`)
38+
console.log(`[handleUri] App name: ${vscode.env.appName}`)
39+
console.log(`[handleUri] Remote name: ${vscode.env.remoteName}`)
40+
console.log(`[handleUri] URI scheme: ${vscode.env.uriScheme}`)
41+
1242
if (!visibleProvider) {
43+
console.warn(`[handleUri] No visible provider found for URI: ${uri.toString()}`)
1344
return
1445
}
1546

1647
switch (path) {
1748
case "/glama": {
1849
const code = query.get("code")
1950
if (code) {
51+
console.log(`[handleUri] Processing Glama callback with code: ${code.substring(0, 10)}...`)
2052
await visibleProvider.handleGlamaCallback(code)
2153
}
2254
break
2355
}
2456
case "/openrouter": {
2557
const code = query.get("code")
2658
if (code) {
59+
console.log(`[handleUri] Processing OpenRouter callback with code: ${code.substring(0, 10)}...`)
2760
await visibleProvider.handleOpenRouterCallback(code)
2861
}
2962
break
3063
}
3164
case "/requesty": {
3265
const code = query.get("code")
3366
if (code) {
67+
console.log(`[handleUri] Processing Requesty callback with code: ${code.substring(0, 10)}...`)
3468
await visibleProvider.handleRequestyCallback(code)
3569
}
3670
break
@@ -40,14 +74,46 @@ export const handleUri = async (uri: vscode.Uri) => {
4074
const state = query.get("state")
4175
const organizationId = query.get("organizationId")
4276

43-
await CloudService.instance.handleAuthCallback(
44-
code,
45-
state,
46-
organizationId === "null" ? null : organizationId,
47-
)
77+
console.log(`[handleUri] Processing Clerk auth callback`)
78+
console.log(`[handleUri] Code present: ${!!code}`)
79+
console.log(`[handleUri] State present: ${!!state}`)
80+
console.log(`[handleUri] Organization ID: ${organizationId}`)
81+
82+
if (isCloudIDE) {
83+
console.log(`[handleUri] Firebase Studio IDE environment detected - using enhanced callback handling`)
84+
85+
// Show user feedback for cloud IDE environments
86+
if (code && state) {
87+
vscode.window.showInformationMessage(
88+
"Authentication callback received in Firebase Studio IDE. Processing login...",
89+
)
90+
}
91+
}
92+
93+
try {
94+
await CloudService.instance.handleAuthCallback(
95+
code,
96+
state,
97+
organizationId === "null" ? null : organizationId,
98+
)
99+
100+
if (isCloudIDE) {
101+
console.log(`[handleUri] Successfully processed auth callback in Firebase Studio IDE`)
102+
}
103+
} catch (error) {
104+
console.error(`[handleUri] Error processing auth callback:`, error)
105+
106+
if (isCloudIDE) {
107+
vscode.window.showErrorMessage(
108+
`Authentication failed in Firebase Studio IDE: ${error instanceof Error ? error.message : String(error)}`,
109+
)
110+
}
111+
throw error
112+
}
48113
break
49114
}
50115
default:
116+
console.log(`[handleUri] Unhandled URI path: ${path}`)
51117
break
52118
}
53119
}

src/extension.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,51 @@ import { initializeI18n } from "./i18n"
5151
let outputChannel: vscode.OutputChannel
5252
let extensionContext: vscode.ExtensionContext
5353

54+
/**
55+
* Detects if running in Firebase Studio IDE (formerly IDX Google) environment
56+
*/
57+
function isFirebaseStudioIDE(): boolean {
58+
const appName = vscode.env.appName?.toLowerCase() || ""
59+
const remoteName = vscode.env.remoteName?.toLowerCase() || ""
60+
61+
return (
62+
appName.includes("idx") ||
63+
appName.includes("firebase") ||
64+
appName.includes("studio") ||
65+
remoteName.includes("idx") ||
66+
remoteName.includes("firebase") ||
67+
process.env.IDX_WORKSPACE_ID !== undefined ||
68+
process.env.FIREBASE_PROJECT_ID !== undefined
69+
)
70+
}
71+
5472
// This method is called when your extension is activated.
5573
// Your extension is activated the very first time the command is executed.
5674
export async function activate(context: vscode.ExtensionContext) {
5775
extensionContext = context
5876
outputChannel = vscode.window.createOutputChannel(Package.outputChannel)
5977
context.subscriptions.push(outputChannel)
78+
79+
// Enhanced logging for Firebase Studio IDE environments
80+
const isCloudIDE = isFirebaseStudioIDE()
6081
outputChannel.appendLine(`${Package.name} extension activated - ${JSON.stringify(Package)}`)
82+
outputChannel.appendLine(`Environment detection:`)
83+
outputChannel.appendLine(` - Firebase Studio IDE: ${isCloudIDE}`)
84+
outputChannel.appendLine(` - App name: ${vscode.env.appName}`)
85+
outputChannel.appendLine(` - Remote name: ${vscode.env.remoteName}`)
86+
outputChannel.appendLine(` - URI scheme: ${vscode.env.uriScheme}`)
87+
outputChannel.appendLine(` - Machine ID: ${vscode.env.machineId}`)
88+
outputChannel.appendLine(` - Session ID: ${vscode.env.sessionId}`)
89+
90+
if (isCloudIDE) {
91+
outputChannel.appendLine(`Firebase Studio IDE environment detected - enabling enhanced authentication handling`)
92+
93+
// Show user notification about Firebase Studio IDE support
94+
vscode.window.showInformationMessage(
95+
"Roo Code detected Firebase Studio IDE environment. Enhanced authentication support is enabled.",
96+
{ modal: false },
97+
)
98+
}
6199

62100
// Migrate old settings to new
63101
await migrateSettings(context, outputChannel)

0 commit comments

Comments
 (0)