diff options
| author | Seth Troisi <[email protected]> | 2025-07-10 18:59:02 -0700 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-07-11 01:59:02 +0000 |
| commit | 8a128d8dc6c1c5d7aea7004e0efa9fd175be36e5 (patch) | |
| tree | cf9d6f3bb06586e6cefe3d72bdd015531f887494 /packages/core/src/code_assist/oauth2.ts | |
| parent | ab66e3a24ebc3ec6c2e8f0c68065680066e265cf (diff) | |
Add NO_BROWSER environment variable to trigger offline oauth flow (#3713)
Diffstat (limited to 'packages/core/src/code_assist/oauth2.ts')
| -rw-r--r-- | packages/core/src/code_assist/oauth2.ts | 98 |
1 files changed, 86 insertions, 12 deletions
diff --git a/packages/core/src/code_assist/oauth2.ts b/packages/core/src/code_assist/oauth2.ts index 93d0e28b..2d3c04d0 100644 --- a/packages/core/src/code_assist/oauth2.ts +++ b/packages/core/src/code_assist/oauth2.ts @@ -4,7 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { OAuth2Client, Credentials, Compute } from 'google-auth-library'; +import { + OAuth2Client, + Credentials, + Compute, + CodeChallengeMethod, +} from 'google-auth-library'; import * as http from 'http'; import url from 'url'; import crypto from 'crypto'; @@ -13,8 +18,10 @@ import open from 'open'; import path from 'node:path'; import { promises as fs, existsSync, readFileSync } from 'node:fs'; import * as os from 'os'; +import { Config } from '../config/config.js'; import { getErrorMessage } from '../utils/errors.js'; import { AuthType } from '../core/contentGenerator.js'; +import readline from 'node:readline'; // OAuth Client ID used to initiate OAuth2Client class. const OAUTH_CLIENT_ID = @@ -57,6 +64,7 @@ export interface OauthWebLogin { export async function getOauthClient( authType: AuthType, + config: Config, ): Promise<OAuth2Client> { const client = new OAuth2Client({ clientId: OAUTH_CLIENT_ID, @@ -109,27 +117,93 @@ export async function getOauthClient( } } - // Otherwise, obtain creds using standard web flow - const webLogin = await authWithWeb(client); + if (config.getNoBrowser()) { + let success = false; + const maxRetries = 2; + for (let i = 0; !success && i < maxRetries; i++) { + success = await authWithUserCode(client); + if (!success) { + console.error( + '\nFailed to authenticate with user code.', + i === maxRetries - 1 ? '' : 'Retrying...\n', + ); + } + } + if (!success) { + process.exit(1); + } + } else { + const webLogin = await authWithWeb(client); - console.log( - `\n\nCode Assist login required.\n` + - `Attempting to open authentication page in your browser.\n` + - `Otherwise navigate to:\n\n${webLogin.authUrl}\n\n`, - ); - await open(webLogin.authUrl); - console.log('Waiting for authentication...'); + // This does basically nothing, as it isn't show to the user. + console.log( + `\n\nCode Assist login required.\n` + + `Attempting to open authentication page in your browser.\n` + + `Otherwise navigate to:\n\n${webLogin.authUrl}\n\n`, + ); + await open(webLogin.authUrl); + console.log('Waiting for authentication...'); - await webLogin.loginCompletePromise; + await webLogin.loginCompletePromise; + } return client; } +async function authWithUserCode(client: OAuth2Client): Promise<boolean> { + const redirectUri = 'https://sdk.cloud.google.com/authcode_cloudcode.html'; + const codeVerifier = await client.generateCodeVerifierAsync(); + const state = crypto.randomBytes(32).toString('hex'); + const authUrl: string = client.generateAuthUrl({ + redirect_uri: redirectUri, + access_type: 'offline', + scope: OAUTH_SCOPE, + code_challenge_method: CodeChallengeMethod.S256, + code_challenge: codeVerifier.codeChallenge, + state, + }); + console.error('Please visit the following URL to authorize the application:'); + console.error(''); + console.error(authUrl); + console.error(''); + + const code = await new Promise<string>((resolve) => { + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + }); + rl.question('Enter the authorization code: ', (answer) => { + rl.close(); + resolve(answer.trim()); + }); + }); + + if (!code) { + console.error('Authorization code is required.'); + return false; + } else { + console.error(`Received authorization code: "${code}"`); + } + + try { + const response = await client.getToken({ + code, + codeVerifier: codeVerifier.codeVerifier, + redirect_uri: redirectUri, + }); + client.setCredentials(response.tokens); + } catch (_error) { + // Consider logging the error. + return false; + } + return true; +} + async function authWithWeb(client: OAuth2Client): Promise<OauthWebLogin> { const port = await getAvailablePort(); const redirectUri = `http://localhost:${port}/oauth2callback`; const state = crypto.randomBytes(32).toString('hex'); - const authUrl: string = client.generateAuthUrl({ + const authUrl = client.generateAuthUrl({ redirect_uri: redirectUri, access_type: 'offline', scope: OAUTH_SCOPE, |
