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}