gcloud_identity_token/
browser.rs

1use crate::shared::get_or_init_port;
2use anyhow::Result;
3use std::collections::HashMap;
4use tiny_http::{Response, Server};
5use url::Url;
6
7pub fn is_headless_env() -> bool {
8    #[cfg(target_os = "macos")]
9    {
10        false
11    }
12    #[cfg(not(target_os = "macos"))]
13    {
14        std::env::var("DISPLAY").is_err() && std::env::var("WAYLAND_DISPLAY").is_err()
15    }
16}
17
18pub fn build_auth_url(client_id: &str, redirect_uri: &str) -> Url {
19    let mut url = Url::parse("https://accounts.google.com/o/oauth2/v2/auth").unwrap();
20    url.query_pairs_mut()
21        .append_pair("client_id", client_id)
22        .append_pair("response_type", "code")
23        .append_pair("scope", "openid email")
24        .append_pair("redirect_uri", redirect_uri)
25        .append_pair("access_type", "offline")
26        .append_pair("include_granted_scopes", "true")
27        .append_pair("prompt", "consent");
28    url
29}
30
31pub fn open_browser_or_print(url: &Url) {
32    if is_headless_env() {
33        eprintln!("\nOpen this URL in your browser:\n\n{url}\n");
34    } else {
35        open::that(url.as_str()).unwrap_or_else(|_| {
36            eprintln!("\nCouldn't open browser. Please open this URL manually:\n\n{url}\n");
37        });
38    }
39}
40
41pub fn capture_auth_code() -> Result<String> {
42    let port = get_or_init_port();
43    let server = Server::http(("127.0.0.1", port)).unwrap();
44    let request = server.recv()?;
45    let query = request.url().split('?').nth(1).unwrap_or("");
46    let params: HashMap<_, _> = url::form_urlencoded::parse(query.as_bytes())
47        .into_owned()
48        .collect();
49    let code = params
50        .get("code")
51        .ok_or("Missing ?code= param")
52        .map_err(|_| anyhow::anyhow!("Error capturing auth code"))?
53        .clone();
54    request.respond(Response::from_string(
55        "You may now return to the application.",
56    ))?;
57    Ok(code)
58}