1use crate::auth::server;
2use anyhow::{Context, Result};
3use oauth2::reqwest::async_http_client;
4use oauth2::{
5 basic::BasicClient, AuthUrl, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl,
6 Scope, TokenResponse, TokenUrl,
7};
8use std::net::TcpListener;
9
10const GEMINI_CLIENT_ID: &str =
12 "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com";
13const GEMINI_CLIENT_SECRET: &str = "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";
14const AUTH_URL: &str = "https://accounts.google.com/o/oauth2/v2/auth";
15const TOKEN_URL: &str = "https://oauth2.googleapis.com/token";
16
17pub async fn authenticate() -> Result<String> {
18 let listener =
20 TcpListener::bind("127.0.0.1:8085").or_else(|_| TcpListener::bind("127.0.0.1:0"))?;
21 let port = listener.local_addr()?.port();
22 let redirect_url = format!("http://localhost:{}/oauth2callback", port);
23
24 let client = BasicClient::new(
25 ClientId::new(GEMINI_CLIENT_ID.to_string()),
26 Some(ClientSecret::new(GEMINI_CLIENT_SECRET.to_string())),
27 AuthUrl::new(AUTH_URL.to_string())?,
28 Some(TokenUrl::new(TOKEN_URL.to_string())?),
29 )
30 .set_redirect_uri(RedirectUrl::new(redirect_url)?);
31
32 let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256();
34
35 let (authorize_url, csrf_state) = client
37 .authorize_url(CsrfToken::new_random)
38 .add_scope(Scope::new(
39 "https://www.googleapis.com/auth/cloud-platform".to_string(),
40 ))
41 .add_scope(Scope::new(
42 "https://www.googleapis.com/auth/userinfo.email".to_string(),
43 ))
44 .add_scope(Scope::new(
45 "https://www.googleapis.com/auth/userinfo.profile".to_string(),
46 ))
47 .add_extra_param("access_type", "offline")
48 .add_extra_param("prompt", "consent")
49 .set_pkce_challenge(pkce_challenge)
50 .url();
51
52 println!("Opening browser for Google authentication...");
53 println!("If browser doesn't open, visit: {}", authorize_url);
54
55 if let Err(e) = open::that(authorize_url.to_string()) {
56 eprintln!(
57 "Failed to open browser: {}. Please open the URL manually.",
58 e
59 );
60 }
61
62 let code = server::run_server(listener, csrf_state.secret().clone()).await?;
64
65 let token_result = client
67 .exchange_code(code)
68 .set_pkce_verifier(pkce_verifier)
69 .request_async(async_http_client)
70 .await
71 .context("Failed to exchange code for token")?;
72
73 if let Some(refresh_token) = token_result.refresh_token() {
75 Ok(refresh_token.secret().clone())
76 } else {
77 Ok(token_result.access_token().secret().clone())
78 }
79}