Skip to main content

gcloud_sdk/
reqwest_auth_middleware.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use reqwest_middleware::{Middleware, Next};
5
6use crate::GoogleAuthTokenGenerator;
7
8/// A `reqwest-middleware` middleware that sets the `authorization` header of every
9/// request to the current token of a [`GoogleAuthTokenGenerator`]: an ID token for a
10/// generator over an [`IdTokenSource`](crate::IdTokenSource), or an access token for one
11/// over Google credentials.
12#[derive(Clone)]
13pub struct GoogleAuthReqwestMiddleware {
14    token_generator: Arc<GoogleAuthTokenGenerator>,
15}
16
17impl GoogleAuthReqwestMiddleware {
18    pub fn new(token_generator: impl Into<Arc<GoogleAuthTokenGenerator>>) -> Self {
19        Self {
20            token_generator: token_generator.into(),
21        }
22    }
23}
24
25#[async_trait]
26impl Middleware for GoogleAuthReqwestMiddleware {
27    async fn handle(
28        &self,
29        mut req: reqwest::Request,
30        extensions: &mut hyper::http::Extensions,
31        next: Next<'_>,
32    ) -> reqwest_middleware::Result<reqwest::Response> {
33        let authorization = self
34            .token_generator
35            .authorization_header()
36            .await
37            .map_err(reqwest_middleware::Error::middleware)?;
38        req.headers_mut()
39            .insert(reqwest::header::AUTHORIZATION, authorization);
40        next.run(req, extensions).await
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47    use crate::test_support::{StubResponse, StubServer};
48    use crate::token_source::{Source, Token};
49    use jiff::{SignedDuration, Timestamp};
50    use secret_vault_value::SecretValue;
51
52    struct FixedToken;
53
54    #[async_trait]
55    impl Source for FixedToken {
56        async fn token(&self) -> crate::error::Result<Token> {
57            Ok(Token::new(
58                "Bearer".to_string(),
59                SecretValue::from("minted-id-token"),
60                Timestamp::now() + SignedDuration::from_hours(1),
61            ))
62        }
63    }
64
65    #[tokio::test]
66    async fn requests_carry_the_generator_token() {
67        let service = StubServer::start(vec![StubResponse::json("200 OK", "{}")]).await;
68        let client = reqwest_middleware::ClientBuilder::new(reqwest::Client::new())
69            .with(GoogleAuthReqwestMiddleware::new(
70                GoogleAuthTokenGenerator::from_source(Box::new(FixedToken) as crate::BoxSource),
71            ))
72            .build();
73
74        let response = client
75            .get(format!("{}/orders", service.url))
76            .send()
77            .await
78            .unwrap();
79
80        assert_eq!(response.status(), reqwest::StatusCode::OK);
81        assert_eq!(
82            service.received()[0].header("authorization"),
83            Some("Bearer minted-id-token")
84        );
85    }
86}