gcloud_auth/token_source/
compute_identity_source.rs1use async_trait::async_trait;
2use jsonwebtoken::Validation;
3use serde::Deserialize;
4use time::OffsetDateTime;
5use urlencoding::encode;
6
7use google_cloud_metadata::{METADATA_FLAVOR_KEY, METADATA_GOOGLE, METADATA_HOST_ENV, METADATA_IP};
8
9use crate::error::Error;
10use crate::token::Token;
11use crate::token_source::{default_http_client, TokenSource};
12
13#[derive(Clone)]
20pub struct ComputeIdentitySource {
21 token_url: String,
22 client: reqwest::Client,
23 decoding_key: jsonwebtoken::DecodingKey,
24 validation: jsonwebtoken::Validation,
25}
26
27impl std::fmt::Debug for ComputeIdentitySource {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 f.debug_struct("ComputeIdentitySource")
30 .field("token_url", &self.token_url)
31 .finish_non_exhaustive()
32 }
33}
34
35impl ComputeIdentitySource {
36 pub(crate) fn new(audience: &str) -> Result<ComputeIdentitySource, Error> {
37 let host = match std::env::var(METADATA_HOST_ENV) {
38 Ok(s) => s,
39 Err(_e) => METADATA_IP.to_string(),
40 };
41
42 let mut validation = Validation::default();
44 validation.insecure_disable_signature_validation();
45 validation.set_audience(&[audience]);
46 let decoding_key = jsonwebtoken::DecodingKey::from_secret(b"");
47
48 Ok(ComputeIdentitySource {
49 token_url: format!(
50 "http://{}/computeMetadata/v1/instance/service-accounts/default/identity?audience={}&format=full",
51 host,
52 encode(audience)
53 ),
54 client: default_http_client(),
55 decoding_key,
56 validation,
57 })
58 }
59}
60
61#[derive(Deserialize)]
62struct ExpClaim {
63 exp: i64,
64}
65
66#[async_trait]
67impl TokenSource for ComputeIdentitySource {
68 async fn token(&self) -> Result<Token, Error> {
69 let jwt = self
70 .client
71 .get(self.token_url.to_string())
72 .header(METADATA_FLAVOR_KEY, METADATA_GOOGLE)
73 .send()
74 .await?
75 .text()
76 .await?;
77
78 let exp = jsonwebtoken::decode::<ExpClaim>(&jwt, &self.decoding_key, &self.validation)?
79 .claims
80 .exp;
81
82 Ok(Token {
83 access_token: jwt,
84 token_type: "Bearer".into(),
85 expiry: OffsetDateTime::from_unix_timestamp(exp).ok(),
86 })
87 }
88}