forked from yoshidan/google-cloud-rust
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompute_identity_source.rs
88 lines (77 loc) · 2.67 KB
/
compute_identity_source.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use async_trait::async_trait;
use jsonwebtoken::Validation;
use serde::Deserialize;
use time::OffsetDateTime;
use urlencoding::encode;
use google_cloud_metadata::{METADATA_FLAVOR_KEY, METADATA_GOOGLE, METADATA_HOST_ENV, METADATA_IP};
use crate::error::Error;
use crate::token::Token;
use crate::token_source::{default_http_client, TokenSource};
/// Fetches a JWT token from the metadata server.
/// using the `identity` endpoint.
///
/// This token source is useful for service-to-service authentication, notably on Cloud Run.
///
/// See <https://cloud.google.com/run/docs/authenticating/service-to-service#use_the_metadata_server>
#[derive(Clone)]
pub struct ComputeIdentitySource {
token_url: String,
client: reqwest::Client,
decoding_key: jsonwebtoken::DecodingKey,
validation: jsonwebtoken::Validation,
}
impl std::fmt::Debug for ComputeIdentitySource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ComputeIdentitySource")
.field("token_url", &self.token_url)
.finish_non_exhaustive()
}
}
impl ComputeIdentitySource {
pub(crate) fn new(audience: &str) -> Result<ComputeIdentitySource, Error> {
let host = match std::env::var(METADATA_HOST_ENV) {
Ok(s) => s,
Err(_e) => METADATA_IP.to_string(),
};
// Only used to extract the expiry without checking the signature.
let mut validation = Validation::default();
validation.insecure_disable_signature_validation();
validation.set_audience(&[audience]);
let decoding_key = jsonwebtoken::DecodingKey::from_secret(b"");
Ok(ComputeIdentitySource {
token_url: format!(
"http://{}/computeMetadata/v1/instance/service-accounts/default/identity?audience={}&format=full",
host,
encode(audience)
),
client: default_http_client(),
decoding_key,
validation,
})
}
}
#[derive(Deserialize)]
struct ExpClaim {
exp: i64,
}
#[async_trait]
impl TokenSource for ComputeIdentitySource {
async fn token(&self) -> Result<Token, Error> {
let jwt = self
.client
.get(self.token_url.to_string())
.header(METADATA_FLAVOR_KEY, METADATA_GOOGLE)
.send()
.await?
.text()
.await?;
let exp = jsonwebtoken::decode::<ExpClaim>(&jwt, &self.decoding_key, &self.validation)?
.claims
.exp;
Ok(Token {
access_token: jwt,
token_type: "Bearer".into(),
expiry: OffsetDateTime::from_unix_timestamp(exp).ok(),
})
}
}