gauth 0.10.1

HTTP Client for Google OAuth2
Documentation
use std::path::Path;

use serde_derive::Deserialize;
use url::Url;

use super::errors::{AuthError, Result};

// Auth code configs
const AUTH_CODE_RESP_TYPE: &str = "code";
const AUTH_CODE_STATE: &str = "pass-through value";
const AUTH_CODE_INCLUDE_GRANTED_SCOPES: &str = "true";
const AUTH_CODE_ACCESS_TYPE: &str = "offline";

#[derive(Deserialize, Debug)]
pub struct OauthConfig {
    pub installed: OauthCredentials,
}

#[derive(Deserialize, Debug)]
pub struct OauthCredentials {
    pub client_id: String,
    // Present in the credentials.json schema; not consumed today but kept
    // on the struct so the layout matches the file. `expect` over `allow`
    // so the suppression auto-fires if a future feature starts reading these.
    #[expect(dead_code)]
    pub project_id: String,
    pub auth_uri: String,
    pub token_uri: String,
    #[expect(dead_code)]
    pub auth_provider_x509_cert_url: String,
    pub client_secret: String,
    pub redirect_uris: Vec<String>,
}

impl OauthCredentials {
    pub fn redirect_uri(&self) -> Result<&String> {
        self.redirect_uris
            .first()
            .ok_or(AuthError::RedirectUriCfgError)
    }
}

pub fn auth_code_uri_str(credentials: &OauthCredentials, scope: &str) -> Result<String> {
    let auth_code_link = auth_code_uri(credentials, scope)?;
    Ok(auth_code_link.to_string())
}

pub fn read_oauth_config(p: &Path) -> Result<OauthConfig> {
    let b = std::fs::read(p)?;
    let cfg = serde_json::from_slice::<OauthConfig>(&b)?;
    Ok(cfg)
}

fn auth_code_uri(credentials: &OauthCredentials, scope: &str) -> Result<url::Url> {
    let mut uri = Url::parse(credentials.auth_uri.as_str())?;

    let params = vec![
        ("client_id", credentials.client_id.as_str()),
        ("response_type", AUTH_CODE_RESP_TYPE),
        ("redirect_uri", credentials.redirect_uri()?),
        ("include_granted_scopes", AUTH_CODE_INCLUDE_GRANTED_SCOPES),
        ("scope", scope),
        ("access_type", AUTH_CODE_ACCESS_TYPE),
        ("state", AUTH_CODE_STATE),
    ];

    for (k, v) in params.into_iter() {
        uri.query_pairs_mut().append_pair(k, v);
    }

    uri.query_pairs_mut().finish();
    Ok(uri)
}