gcloud_auth/token_source/
mod.rs1use std::fmt::Debug;
2use std::time::Duration;
3
4use async_trait::async_trait;
5use jsonwebtoken;
6use serde::Deserialize;
7
8use crate::error::Error;
9use crate::token::Token;
10
11pub mod authorized_user_token_source;
12pub mod compute_identity_source;
13pub mod compute_token_source;
14pub mod impersonate_token_source;
15pub mod reuse_token_source;
16pub mod service_account_token_source;
17
18#[cfg(feature = "external-account")]
19pub mod external_account_source;
20
21#[async_trait]
22pub trait TokenSource: Send + Sync + Debug {
23 async fn token(&self) -> Result<Token, Error>;
24}
25
26pub(crate) fn default_http_client() -> reqwest::Client {
27 reqwest::Client::builder()
28 .timeout(Duration::from_secs(3))
29 .build()
30 .unwrap()
31}
32
33#[derive(Clone, Deserialize)]
34struct InternalToken {
35 pub access_token: String,
36 pub token_type: String,
37 pub expires_in: Option<i64>,
38}
39
40impl InternalToken {
41 fn to_token(&self, now: time::OffsetDateTime) -> Token {
42 Token {
43 access_token: self.access_token.clone(),
44 token_type: self.token_type.clone(),
45 expiry: self.expires_in.map(|s| now + time::Duration::seconds(s)),
46 }
47 }
48}
49
50#[derive(Clone, Deserialize)]
51struct InternalIdToken {
52 pub id_token: String,
53}
54
55#[derive(Deserialize)]
56struct ExpClaim {
57 exp: i64,
58}
59
60impl InternalIdToken {
61 fn to_token(&self, audience: &str) -> Result<Token, Error> {
62 Ok(Token {
63 access_token: self.id_token.clone(),
64 token_type: "Bearer".into(),
65 expiry: time::OffsetDateTime::from_unix_timestamp(self.get_exp(audience)?).ok(),
66 })
67 }
68
69 fn get_exp(&self, _audience: &str) -> Result<i64, Error> {
70 let token = jsonwebtoken::dangerous::insecure_decode::<ExpClaim>(self.id_token.as_bytes())?;
72 Ok(token.claims.exp)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use crate::credentials::CredentialsFile;
79 use crate::error::Error;
80 use crate::token_source::service_account_token_source::{
81 OAuth2ServiceAccountTokenSource, ServiceAccountTokenSource,
82 };
83 use crate::token_source::TokenSource;
84
85 #[tokio::test]
86 async fn test_jwt_token_source() -> Result<(), Error> {
87 let credentials = CredentialsFile::new().await?;
88 let audience = "https://spanner.googleapis.com/";
89 let ts = ServiceAccountTokenSource::new(&credentials, audience)?;
90 let token = ts.token().await?;
91 assert_eq!("Bearer", token.token_type);
92 assert!(token.expiry.unwrap().unix_timestamp() > 0);
93 Ok(())
94 }
95
96 #[tokio::test]
97 async fn test_oauth2_token_source() -> Result<(), Error> {
98 let credentials = CredentialsFile::new().await?;
99 let scope = "https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/spanner.data";
100 let sub = None;
101 let ts = OAuth2ServiceAccountTokenSource::new(&credentials, scope, sub)?;
102 let token = ts.token().await?;
103 assert_eq!("Bearer", token.token_type);
104 assert!(token.expiry.unwrap().unix_timestamp() > 0);
105 Ok(())
106 }
107}