mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: add manual token authentication for Firebase Studio support
- Add manual token input UI to CloudView component with expandable section - Add new WebviewMessage type 'rooCloudManualToken' with token field - Implement handleManualToken method in WebAuthService to process manual tokens - Add CloudService.handleManualToken method to route manual token auth - Update webviewMessageHandler to handle manual token submission - Add translation strings for manual token UI elements This allows users in Firebase Studio and other IDEs that don't support automatic authentication redirects to manually paste the token from the authentication page. Fixes #7723
This commit is contained in:
parent
e8deedd91b
commit
7284fd0b62
6 changed files with 152 additions and 4 deletions
|
|
@ -216,6 +216,20 @@ export class CloudService extends EventEmitter<CloudServiceEvents> implements Di
|
|||
return this.authService!.handleCallback(code, state, organizationId)
|
||||
}
|
||||
|
||||
public async handleManualToken(token: string): Promise<void> {
|
||||
this.ensureInitialized()
|
||||
if (!this.authService) {
|
||||
throw new Error("Auth service not available")
|
||||
}
|
||||
// For WebAuthService, we need to add a method to handle manual tokens
|
||||
// Type guard to check if the auth service is WebAuthService
|
||||
if (this.authService instanceof WebAuthService) {
|
||||
return this.authService.handleManualToken(token)
|
||||
} else {
|
||||
throw new Error("Manual token authentication not supported with current auth service")
|
||||
}
|
||||
}
|
||||
|
||||
// SettingsService
|
||||
|
||||
public getAllowList(): OrganizationAllowList {
|
||||
|
|
|
|||
|
|
@ -330,6 +330,42 @@ export class WebAuthService extends EventEmitter<AuthServiceEvents> implements A
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle manual token input
|
||||
*
|
||||
* This method allows users to manually paste a token from the authentication page
|
||||
* when automatic redirect doesn't work (e.g., in Firebase Studio)
|
||||
*
|
||||
* @param token The authentication token from the URL
|
||||
*/
|
||||
public async handleManualToken(token: string): Promise<void> {
|
||||
if (!token || !token.trim()) {
|
||||
throw new Error("Invalid token provided")
|
||||
}
|
||||
|
||||
try {
|
||||
// The token is the ticket that we need to exchange for credentials
|
||||
const credentials = await this.clerkSignIn(token.trim())
|
||||
|
||||
// For manual token, we don't have organization context from the URL
|
||||
// Set it to null (personal account) by default
|
||||
credentials.organizationId = null
|
||||
|
||||
await this.storeCredentials(credentials)
|
||||
|
||||
const vscode = await importVscode()
|
||||
|
||||
if (vscode) {
|
||||
vscode.window.showInformationMessage("Successfully authenticated with Roo Code Cloud via manual token")
|
||||
}
|
||||
|
||||
this.log("[auth] Successfully authenticated with Roo Code Cloud via manual token")
|
||||
} catch (error) {
|
||||
this.log(`[auth] Error handling manual token: ${error}`)
|
||||
throw new Error(`Failed to authenticate with manual token: ${error}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2251,6 +2251,35 @@ export const webviewMessageHandler = async (
|
|||
|
||||
break
|
||||
}
|
||||
case "rooCloudManualToken": {
|
||||
if (message.token) {
|
||||
try {
|
||||
// Extract the actual token from the URL if a full URL is pasted
|
||||
let token = message.token.trim()
|
||||
const tokenMatch = token.match(/[?&]token=([^&]+)/)
|
||||
if (tokenMatch) {
|
||||
token = tokenMatch[1]
|
||||
}
|
||||
|
||||
// Call the manual token authentication method
|
||||
await CloudService.instance.handleManualToken(token)
|
||||
await provider.postStateToWebview()
|
||||
provider.postMessageToWebview({
|
||||
type: "authenticatedUser",
|
||||
userInfo: CloudService.instance.getUserInfo(),
|
||||
})
|
||||
} catch (error) {
|
||||
provider.log(
|
||||
`Manual token authentication failed: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
vscode.window.showErrorMessage(
|
||||
t("common:errors.manual_token_auth_failed") ||
|
||||
"Manual token authentication failed. Please check the token and try again.",
|
||||
)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case "saveCodeIndexSettingsAtomic": {
|
||||
if (!message.codeIndexSettings) {
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ export interface WebviewMessage {
|
|||
| "cloudButtonClicked"
|
||||
| "rooCloudSignIn"
|
||||
| "rooCloudSignOut"
|
||||
| "rooCloudManualToken"
|
||||
| "condenseTaskContextRequest"
|
||||
| "requestIndexingStatus"
|
||||
| "startIndexing"
|
||||
|
|
@ -222,6 +223,7 @@ export interface WebviewMessage {
|
|||
| "removeQueuedMessage"
|
||||
| "editQueuedMessage"
|
||||
text?: string
|
||||
token?: string
|
||||
editedMessageContent?: string
|
||||
tab?: "settings" | "history" | "mcp" | "modes" | "chat" | "marketplace" | "cloud"
|
||||
disabled?: boolean
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useRef } from "react"
|
||||
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
import { VSCodeButton, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
|
||||
|
||||
import { type CloudUserInfo, TelemetryEventName } from "@roo-code/types"
|
||||
|
||||
|
|
@ -9,7 +9,7 @@ import { vscode } from "@src/utils/vscode"
|
|||
import { telemetryClient } from "@src/utils/TelemetryClient"
|
||||
import { ToggleSwitch } from "@/components/ui/toggle-switch"
|
||||
|
||||
import { History, PiggyBank, SquareArrowOutUpRightIcon } from "lucide-react"
|
||||
import { History, PiggyBank, SquareArrowOutUpRightIcon, Key } from "lucide-react"
|
||||
|
||||
// Define the production URL constant locally to avoid importing from cloud package in tests
|
||||
const PRODUCTION_ROO_CODE_API_URL = "https://app.roocode.com"
|
||||
|
|
@ -25,6 +25,9 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: Cl
|
|||
const { t } = useAppTranslation()
|
||||
const { remoteControlEnabled, setRemoteControlEnabled } = useExtensionState()
|
||||
const wasAuthenticatedRef = useRef(false)
|
||||
const [showManualToken, setShowManualToken] = useState(false)
|
||||
const [manualToken, setManualToken] = useState("")
|
||||
const [tokenError, setTokenError] = useState("")
|
||||
|
||||
const rooLogoUri = (window as any).IMAGES_BASE_URI + "/roo-logo.svg"
|
||||
|
||||
|
|
@ -75,6 +78,26 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: Cl
|
|||
vscode.postMessage({ type: "remoteControlEnabled", bool: newValue })
|
||||
}
|
||||
|
||||
const handleManualTokenSubmit = () => {
|
||||
if (!manualToken.trim()) {
|
||||
setTokenError(t("cloud:manualTokenError") || "Please enter a valid token")
|
||||
return
|
||||
}
|
||||
setTokenError("")
|
||||
// Extract the token from the URL if a full URL is pasted
|
||||
let token = manualToken.trim()
|
||||
const tokenMatch = token.match(/[?&]token=([^&]+)/)
|
||||
if (tokenMatch) {
|
||||
token = tokenMatch[1]
|
||||
}
|
||||
|
||||
// Send the manual token to the backend
|
||||
vscode.postMessage({ type: "rooCloudManualToken", token })
|
||||
// Clear the token field for security
|
||||
setManualToken("")
|
||||
setShowManualToken(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex justify-between items-center mb-6">
|
||||
|
|
@ -192,6 +215,45 @@ export const CloudView = ({ userInfo, isAuthenticated, cloudApiUrl, onDone }: Cl
|
|||
<VSCodeButton appearance="primary" onClick={handleConnectClick} className="w-1/2">
|
||||
{t("cloud:connect")}
|
||||
</VSCodeButton>
|
||||
|
||||
{/* Manual token input section */}
|
||||
<div className="w-full mt-4">
|
||||
<button
|
||||
onClick={() => setShowManualToken(!showManualToken)}
|
||||
className="text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground underline cursor-pointer bg-transparent border-none p-0 text-sm flex items-center gap-1">
|
||||
<Key size="14" />
|
||||
{t("cloud:manualTokenLink") || "Having trouble? Enter token manually"}
|
||||
</button>
|
||||
|
||||
{showManualToken && (
|
||||
<div className="mt-4 p-4 border border-vscode-widget-border rounded">
|
||||
<p className="text-sm text-vscode-descriptionForeground mb-3">
|
||||
{t("cloud:manualTokenDescription") ||
|
||||
"If you're using Firebase Studio or another IDE that doesn't support automatic authentication, you can paste the token from the authentication page here."}
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<VSCodeTextField
|
||||
value={manualToken}
|
||||
onInput={(e: any) => {
|
||||
setManualToken(e.target.value)
|
||||
setTokenError("")
|
||||
}}
|
||||
placeholder={t("cloud:manualTokenPlaceholder") || "Paste token or URL here"}
|
||||
className="flex-1"
|
||||
/>
|
||||
<VSCodeButton
|
||||
appearance="secondary"
|
||||
onClick={handleManualTokenSubmit}
|
||||
disabled={!manualToken.trim()}>
|
||||
{t("cloud:submitToken") || "Submit"}
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
{tokenError && (
|
||||
<p className="text-sm text-vscode-errorForeground mt-2">{tokenError}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -13,5 +13,10 @@
|
|||
"visitCloudWebsite": "Visit Roo Code Cloud",
|
||||
"remoteControl": "Roomote Control",
|
||||
"remoteControlDescription": "Enable following and interacting with tasks in this workspace with Roo Code Cloud",
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL"
|
||||
"cloudUrlPillLabel": "Roo Code Cloud URL",
|
||||
"manualTokenLink": "Having trouble? Enter token manually",
|
||||
"manualTokenDescription": "If you're using Firebase Studio or another IDE that doesn't support automatic authentication, you can paste the token from the authentication page here.",
|
||||
"manualTokenPlaceholder": "Paste token or URL here",
|
||||
"manualTokenError": "Please enter a valid token",
|
||||
"submitToken": "Submit"
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue