Skip to content

Commit d66a9dc

Browse files
committed
fix(auth): complete Google OAuth callbacks in noninteractive login
1 parent c45c4f3 commit d66a9dc

2 files changed

Lines changed: 44 additions & 1 deletion

File tree

src/cli/login.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,13 @@ pub async fn run_login_provider(
257257
provider.display_name
258258
);
259259
}
260-
start_scriptable_login(provider, account_label, &options).await
260+
if provider.target == LoginProviderTarget::Google
261+
&& !auth::browser_suppressed(options.no_browser)
262+
{
263+
run_automatic_google_login(provider.id, &options).await
264+
} else {
265+
start_scriptable_login(provider, account_label, &options).await
266+
}
261267
} else {
262268
match provider.target {
263269
LoginProviderTarget::AutoImport => {

src/cli/login/scriptable.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,43 @@ pub(super) async fn run_scriptable_login_provider(
4949
complete_scriptable_login(provider, account_label, options, input).await
5050
}
5151

52+
/// Run the normal Google OAuth flow when the caller is noninteractive but a
53+
/// browser is available. The scriptable flow is normally used in that
54+
/// environment to avoid prompts, but Google needs a live localhost callback
55+
/// listener or the browser lands on a confusing connection error page.
56+
pub(super) async fn run_automatic_google_login(
57+
provider_id: &str,
58+
options: &LoginOptions,
59+
) -> Result<LoginFlowOutcome> {
60+
let tier = options
61+
.google_access_tier
62+
.unwrap_or(auth::google::GmailAccessTier::Full);
63+
let tokens = auth::google::login(tier, options.no_browser).await?;
64+
let credentials_path = auth::google::credentials_path()?;
65+
let tokens_path = auth::google::tokens_path()?;
66+
67+
emit_scriptable_auth_success(
68+
options.json,
69+
ScriptableAuthSuccess {
70+
status: "authenticated",
71+
provider: provider_id.to_string(),
72+
account_label: None,
73+
credentials_path: Some(credentials_path.display().to_string()),
74+
email: tokens.email.clone(),
75+
},
76+
)?;
77+
if !options.json {
78+
eprintln!("\nGmail setup complete!");
79+
if let Some(email) = tokens.email {
80+
eprintln!("Account: {}", email);
81+
}
82+
eprintln!("Access tier: {}", tokens.tier.label());
83+
eprintln!("Tokens saved to {}", tokens_path.display());
84+
}
85+
crate::telemetry::record_auth_success(provider_id, "oauth");
86+
Ok(LoginFlowOutcome::Completed)
87+
}
88+
5289
pub(super) async fn start_scriptable_login(
5390
provider: LoginProviderDescriptor,
5491
account_label: Option<&str>,

0 commit comments

Comments
 (0)