gcloud_identity_token/
config.rs

1//! Configuration types and helpers for working with Google OAuth credentials.
2//!
3//! This module defines the key data structures used during OAuth flows and
4//! provides a helper to load credentials from the user's local environment.
5
6use anyhow::Result;
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10/// Represents OAuth client credentials used to initiate the authorization flow.
11///
12/// These credentials are typically loaded from a JSON file located at:
13/// `~/.config/gcloud/application_default_credentials.json`
14#[derive(Deserialize)]
15pub struct Creds {
16    /// OAuth 2.0 client ID
17    pub client_id: String,
18    /// OAuth 2.0 client secret
19    pub client_secret: String,
20}
21
22/// A token response received from Google's OAuth token endpoint.
23///
24/// This includes the access token, ID token, optional refresh token,
25/// and the token expiration duration (in seconds).
26#[derive(Deserialize)]
27pub struct TokenResponse {
28    /// OAuth 2.0 access token used for Google APIs
29    pub access_token: String,
30    /// OpenID Connect ID token (JWT) containing user identity
31    pub id_token: String,
32    /// Refresh token (only returned during first login)
33    #[serde(default)]
34    pub refresh_token: Option<String>,
35    /// Time until expiration in seconds
36    pub expires_in: i64,
37}
38
39/// Output returned by the library to the user after successful authentication.
40///
41/// This structure is printed as JSON and includes only the fields necessary
42/// for downstream use (access, identity, and expiration).
43#[derive(Serialize)]
44pub struct TokenOutput<'a> {
45    /// OAuth 2.0 access token
46    pub access_token: &'a str,
47    /// ID token (JWT) identifying the user
48    pub id_token: &'a str,
49    /// UTC expiry timestamp
50    pub token_expiry: DateTime<Utc>,
51}
52
53/// A saved token cached on disk for future reuse.
54///
55/// This includes the refresh token, current access and ID tokens,
56/// and their expiration timestamp.
57#[derive(Serialize, Deserialize)]
58pub struct SavedToken {
59    /// Long-lived refresh token for future access
60    pub refresh_token: String,
61    /// Most recently issued access token
62    pub access_token: String,
63    /// Most recently issued ID token
64    pub id_token: String,
65    /// Expiration timestamp of the token
66    pub token_expiry: DateTime<Utc>,
67}
68
69/// Loads the user's OAuth 2.0 credentials from the default gcloud location.
70///
71/// This typically reads the file:
72/// `~/.config/gcloud/application_default_credentials.json`
73///
74/// # Errors
75///
76/// Returns an error if the file is missing, unreadable, or invalid JSON.
77pub fn load_creds() -> Result<Creds> {
78    let path = dirs::home_dir()
79        .ok_or("Could not determine home directory")
80        .map_err(|_| anyhow::anyhow!("Home directory not found"))?
81        .join(".config/gcloud/application_default_credentials.json");
82
83    let creds = std::fs::read_to_string(path)?;
84    Ok(serde_json::from_str(&creds)?)
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_parse_valid_creds() {
93        let json = r#"{
94            "client_id": "abc123",
95            "client_secret": "secret"
96        }"#;
97        let creds: Creds = serde_json::from_str(json).unwrap();
98        assert_eq!(creds.client_id, "abc123");
99    }
100
101    #[test]
102    fn test_missing_field_fails() {
103        let json = r#"{
104            "client_id": "abc123"
105        }"#;
106        let result: Result<Creds, _> = serde_json::from_str(json);
107        assert!(result.is_err());
108    }
109}