1use async_trait::async_trait;
2use http::Extensions;
3use reqwest::header::{AUTHORIZATION, CONTENT_TYPE};
4use reqwest::{Request, Response};
5use reqwest_middleware::{Middleware, Next, Result as MiddlewareResult};
6use std::sync::Arc;
7use std::sync::Mutex;
8
9pub struct TokenMiddleware {
10 client_id: String,
11 client_secret: String,
12 token_url: String,
13 token_cache: Arc<Mutex<Option<String>>>,
14}
15
16impl TokenMiddleware {
17 pub fn new(client_id: String, client_secret: String, token_url: String) -> Self {
18 Self {
19 client_id,
20 client_secret,
21 token_url,
22 token_cache: Arc::new(Mutex::new(None)),
23 }
24 }
25
26 async fn fetch_token(&self) -> Option<String> {
27 let client_id = &self.client_id;
28 let client_secret = &self.client_secret;
29 let token_url = &self.token_url;
30 let params = [
31 ("grant_type", "client_credentials"),
32 ("client_id", client_id.as_str()),
33 ("client_secret", client_secret.as_str()),
34 ];
35 let client = reqwest::Client::builder()
36 .danger_accept_invalid_certs(true)
37 .build()
38 .ok()?;
39 let resp = client
40 .post(token_url)
41 .header(CONTENT_TYPE, "application/x-www-form-urlencoded")
42 .form(¶ms)
43 .send()
44 .await
45 .ok()?;
46 if resp.status().is_success() {
47 let json: serde_json::Value = resp.json().await.ok()?;
48 json.get("access_token")
49 .and_then(|v| v.as_str())
50 .map(|s| s.to_string())
51 } else {
52 None
53 }
54 }
55}
56
57#[async_trait]
58impl Middleware for TokenMiddleware {
59 async fn handle(
60 &self,
61 mut req: Request,
62 extensions: &mut Extensions,
63 next: Next<'_>,
64 ) -> MiddlewareResult<Response> {
65 let token_opt = {
67 let token_guard = self.token_cache.lock().unwrap();
68 token_guard.clone()
69 };
70 let token = if let Some(token) = token_opt {
71 tracing::info!("Token returning from cache");
72 token
73 } else {
74 let token = self.fetch_token().await;
75 let mut token_guard = self.token_cache.lock().unwrap();
76 *token_guard = token.clone();
77
78 tracing::info!("Fetching new token from keycloak");
79
80 token.unwrap_or_default()
81 };
82 if !token.is_empty() {
83 req.headers_mut()
84 .insert(AUTHORIZATION, format!("Bearer {}", token).parse().unwrap());
85 tracing::info!("Token set in request headers");
86 }
87 next.run(req, extensions).await
88 }
89}