Skip to content

Commit 2ee010a

Browse files
konardclaude
andcommitted
fix(auth): enable Gemini OAuth callback via Docker port forwarding
The OAuth flow now works correctly in Docker environments: - Use fixed callback port (38751) for consistent Docker port forwarding - Add OAUTH_CALLBACK_PORT and OAUTH_CALLBACK_HOST environment variables - Map the port between host and container (-p 38751:38751) - Add --debug flag to ensure auth URL is displayed - Print helpful OAuth instructions before starting the flow This addresses the user's request to support OAuth where the callback URL (http://127.0.0.1:PORT/oauth2callback?...) is automatically captured via the forwarded port instead of requiring manual URL pasting. Fixes #146 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 23c03ba commit 2ee010a

File tree

1 file changed

+50
-6
lines changed

1 file changed

+50
-6
lines changed

packages/lib/src/usecases/auth-gemini-oauth.ts

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,18 @@ const detectAuthResult = (output: string): GeminiAuthResult => {
6060
return "pending"
6161
}
6262

63+
// Fixed port for Gemini CLI OAuth callback server
64+
// WHY: Using a fixed port allows Docker port forwarding to work
65+
// SOURCE: https://github.com/google-gemini/gemini-cli/issues/2040
66+
const geminiOauthCallbackPort = 38_751
67+
6368
type DockerGeminiAuthSpec = {
6469
readonly cwd: string
6570
readonly image: string
6671
readonly hostPath: string
6772
readonly containerPath: string
6873
readonly env: ReadonlyArray<string>
74+
readonly callbackPort: number
6975
}
7076

7177
const buildDockerGeminiAuthSpec = (
@@ -78,15 +84,27 @@ const buildDockerGeminiAuthSpec = (
7884
image,
7985
hostPath: accountPath,
8086
containerPath,
87+
callbackPort: geminiOauthCallbackPort,
8188
env: [
8289
`HOME=${containerPath}`,
8390
"NO_BROWSER=true",
84-
"GEMINI_CLI_NONINTERACTIVE=false"
91+
"GEMINI_CLI_NONINTERACTIVE=false",
92+
`OAUTH_CALLBACK_PORT=${geminiOauthCallbackPort}`,
93+
"OAUTH_CALLBACK_HOST=0.0.0.0"
8594
]
8695
})
8796

8897
const buildDockerGeminiAuthArgs = (spec: DockerGeminiAuthSpec): ReadonlyArray<string> => {
89-
const base: Array<string> = ["run", "--rm", "-i", "-t", "-v", `${spec.hostPath}:${spec.containerPath}`]
98+
const base: Array<string> = [
99+
"run",
100+
"--rm",
101+
"-i",
102+
"-t",
103+
"-v",
104+
`${spec.hostPath}:${spec.containerPath}`,
105+
"-p",
106+
`${spec.callbackPort}:${spec.callbackPort}`
107+
]
90108
const dockerUser = resolveDefaultDockerUser()
91109
if (dockerUser !== null) {
92110
base.push("--user", dockerUser)
@@ -98,8 +116,10 @@ const buildDockerGeminiAuthArgs = (spec: DockerGeminiAuthSpec): ReadonlyArray<st
98116
}
99117
base.push("-e", trimmed)
100118
}
101-
// Run gemini CLI - it will prompt for OAuth authentication with NO_BROWSER=true
102-
return [...base, spec.image, "gemini"]
119+
// Run gemini CLI with --debug flag to ensure auth URL is shown
120+
// WHY: In some Gemini CLI versions, auth URL is only shown with --debug flag
121+
// SOURCE: https://github.com/google-gemini/gemini-cli/issues/13853
122+
return [...base, spec.image, "gemini", "--debug"]
103123
}
104124

105125
const startDockerProcess = (
@@ -179,8 +199,30 @@ const resolveGeminiLoginResult = (
179199
// (user may have completed auth flow successfully)
180200
})
181201

182-
// CHANGE: run Gemini CLI OAuth login with interactive prompt
183-
// WHY: Gemini CLI with NO_BROWSER=true shows auth URL and waits for user to paste authorization code
202+
// CHANGE: print OAuth instructions before starting the flow
203+
// WHY: help users understand how to complete OAuth in Docker environment
204+
// QUOTE(ТЗ): "Мне надо что бы он её умел принимать, типо ждал пока мы вставим ссылку"
205+
// REF: issue-146, PR-147 comment from skulidropek
206+
// SOURCE: https://github.com/google-gemini/gemini-cli
207+
// PURITY: SHELL
208+
// COMPLEXITY: O(1)
209+
const printOauthInstructions = (): Effect.Effect<void> =>
210+
Effect.sync(() => {
211+
const port = geminiOauthCallbackPort
212+
process.stderr.write("\n")
213+
process.stderr.write("╔═══════════════════════════════════════════════════════════════════════════╗\n")
214+
process.stderr.write("║ Gemini CLI OAuth Authentication ║\n")
215+
process.stderr.write("╠═══════════════════════════════════════════════════════════════════════════╣\n")
216+
process.stderr.write("║ 1. Copy the auth URL shown below and open it in your browser ║\n")
217+
process.stderr.write("║ 2. Sign in with your Google account ║\n")
218+
process.stderr.write(`║ 3. After authentication, the browser will redirect to localhost:${port} ║\n`)
219+
process.stderr.write("║ 4. The callback will be captured automatically (port is forwarded) ║\n")
220+
process.stderr.write("╚═══════════════════════════════════════════════════════════════════════════╝\n")
221+
process.stderr.write("\n")
222+
})
223+
224+
// CHANGE: run Gemini CLI OAuth login with interactive prompt and port forwarding
225+
// WHY: Gemini CLI OAuth callback now works in Docker via fixed port forwarding
184226
// QUOTE(ТЗ): "Типо ждал пока мы вставим ссылку"
185227
// REF: issue-146, PR-147 comment
186228
// SOURCE: https://github.com/google-gemini/gemini-cli
@@ -199,6 +241,8 @@ export const runGeminiOauthLoginWithPrompt = (
199241
): Effect.Effect<void, AuthError | CommandFailedError | PlatformError, CommandExecutor.CommandExecutor> =>
200242
Effect.scoped(
201243
Effect.gen(function*(_) {
244+
yield* _(printOauthInstructions())
245+
202246
const executor = yield* _(CommandExecutor.CommandExecutor)
203247
const hostPath = yield* _(resolveDockerVolumeHostPath(cwd, accountPath))
204248
const spec = buildDockerGeminiAuthSpec(cwd, hostPath, options.image, options.containerPath)

0 commit comments

Comments
 (0)