1use std::pin::Pin;
23use std::sync::Arc;
24use std::task::{Context, Poll};
25
26use axum_core::extract::FromRequestParts;
27use futures::Future;
28use hyper::header::{HeaderMap, HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE};
29use hyper::http::request::Parts;
30use hyper::{Request, Response, StatusCode};
31use tower::Service;
32use tower_layer::Layer;
33use tracing::*;
34
35use crate::id_token_verify::{IdTokenVerifier, IdTokenVerifyError, VerifiedIdToken};
36
37type Authorize = dyn Fn(&VerifiedIdToken) -> bool + Send + Sync;
38
39#[derive(Clone)]
50pub struct VerifyIdTokenLayer {
51 verifier: Arc<IdTokenVerifier>,
52 authorize: Option<Arc<Authorize>>,
53}
54
55impl VerifyIdTokenLayer {
56 pub fn new(verifier: Arc<IdTokenVerifier>) -> Self {
58 Self {
59 verifier,
60 authorize: None,
61 }
62 }
63
64 pub fn authorize(
67 mut self,
68 authorize: impl Fn(&VerifiedIdToken) -> bool + Send + Sync + 'static,
69 ) -> Self {
70 self.authorize = Some(Arc::new(authorize));
71 self
72 }
73
74 async fn admit(&self, headers: &HeaderMap) -> Result<VerifiedIdToken, Refusal> {
75 let token = bearer_token(headers).ok_or(Refusal::MissingToken)?;
76 let verified = self.verifier.verify(token).await.map_err(|err| match err {
77 IdTokenVerifyError::InvalidToken(reason) => {
78 debug!(%reason, "Refusing a request with an invalid ID token");
79 Refusal::InvalidToken
80 }
81 IdTokenVerifyError::KeysUnavailable(error) => {
82 warn!(%error, "Refusing a request: ID token signing keys are unavailable");
83 Refusal::KeysUnavailable
84 }
85 })?;
86 match &self.authorize {
87 Some(authorize) if !authorize(&verified) => Err(Refusal::Forbidden),
88 _ => Ok(verified),
89 }
90 }
91}
92
93impl<S> Layer<S> for VerifyIdTokenLayer {
94 type Service = VerifyIdToken<S>;
95
96 fn layer(&self, inner: S) -> Self::Service {
97 VerifyIdToken {
98 inner,
99 layer: self.clone(),
100 }
101 }
102}
103
104#[derive(Clone)]
106pub struct VerifyIdToken<S> {
107 inner: S,
108 layer: VerifyIdTokenLayer,
109}
110
111impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for VerifyIdToken<S>
112where
113 S: Service<Request<ReqBody>, Response = Response<ResBody>> + Clone + Send + 'static,
114 S::Future: Send + 'static,
115 ReqBody: Send + 'static,
116 ResBody: Default + Send + 'static,
117{
118 type Response = Response<ResBody>;
119 type Error = S::Error;
120 type Future =
121 Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
122
123 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
124 self.inner.poll_ready(cx)
125 }
126
127 fn call(&mut self, mut req: Request<ReqBody>) -> Self::Future {
128 let layer = self.layer.clone();
129 let clone = self.inner.clone();
132 let mut inner = std::mem::replace(&mut self.inner, clone);
133
134 Box::pin(async move {
135 match layer.admit(req.headers()).await {
136 Ok(verified) => {
137 req.extensions_mut().insert(verified);
138 inner.call(req).await
139 }
140 Err(refusal) => Ok(refusal.into()),
141 }
142 })
143 }
144}
145
146enum Refusal {
148 MissingToken,
149 InvalidToken,
150 KeysUnavailable,
151 Forbidden,
152}
153
154impl<B: Default> From<Refusal> for Response<B> {
155 fn from(refusal: Refusal) -> Self {
156 let (status, challenge) = match refusal {
157 Refusal::MissingToken => (StatusCode::UNAUTHORIZED, Some("Bearer")),
158 Refusal::InvalidToken => (
159 StatusCode::UNAUTHORIZED,
160 Some(r#"Bearer error="invalid_token""#),
161 ),
162 Refusal::KeysUnavailable => (StatusCode::SERVICE_UNAVAILABLE, None),
163 Refusal::Forbidden => (StatusCode::FORBIDDEN, None),
164 };
165 let mut response = Response::new(B::default());
166 *response.status_mut() = status;
167 if let Some(challenge) = challenge {
168 response
169 .headers_mut()
170 .insert(WWW_AUTHENTICATE, HeaderValue::from_static(challenge));
171 }
172 response
173 }
174}
175
176fn bearer_token(headers: &HeaderMap) -> Option<&str> {
179 let (scheme, token) = headers.get(AUTHORIZATION)?.to_str().ok()?.split_once(' ')?;
180 let token = token.trim();
181 (scheme.eq_ignore_ascii_case("bearer") && !token.is_empty()).then_some(token)
182}
183
184impl<S: Send + Sync> FromRequestParts<S> for VerifiedIdToken {
189 type Rejection = StatusCode;
190
191 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
192 parts
193 .extensions
194 .get::<VerifiedIdToken>()
195 .cloned()
196 .ok_or(StatusCode::INTERNAL_SERVER_ERROR)
197 }
198}
199
200#[cfg(all(test, any(feature = "jwt-aws-lc-rs", feature = "jwt-rust-crypto")))]
202mod tests {
203 use super::*;
204 use crate::error::ErrorKind;
205 use crate::id_token_verify::{IdTokenKeys, IdTokenKeysSource, PrincipalEmail};
206 use crate::test_support::{signed_jwt, test_jwk_set};
207 use crate::IdTokenAudience;
208 use async_trait::async_trait;
209 use jiff::{SignedDuration, Timestamp};
210 use std::convert::Infallible;
211 use std::time::Duration;
212
213 const AUDIENCE: &str = "https://orders-abc123-ew.a.run.app";
214 const CALLER: &str = "caller@my-project.iam.gserviceaccount.com";
215 const KEY_ID: &str = "current-key";
216
217 enum TestKeys {
218 Published,
219 Unavailable,
220 }
221
222 #[async_trait]
223 impl IdTokenKeysSource for TestKeys {
224 async fn fetch_keys(&self) -> crate::error::Result<IdTokenKeys> {
225 match self {
226 Self::Published => Ok(IdTokenKeys::new(
227 test_jwk_set(&[KEY_ID]),
228 Duration::from_secs(3600),
229 )),
230 Self::Unavailable => {
231 Err(ErrorKind::HttpStatus(reqwest::StatusCode::SERVICE_UNAVAILABLE).into())
232 }
233 }
234 }
235 }
236
237 fn verifier(keys: TestKeys) -> Arc<IdTokenVerifier> {
238 Arc::new(IdTokenVerifier::with_keys_source(
239 IdTokenAudience::new(AUDIENCE),
240 keys,
241 ))
242 }
243
244 fn token_for(audience: &str) -> String {
245 let now = Timestamp::now();
246 let claims = serde_json::json!({
247 "iss": "https://accounts.google.com",
248 "aud": audience,
249 "sub": "112233445566778899",
250 "email": CALLER,
251 "email_verified": true,
252 "iat": now.as_second(),
253 "exp": (now + SignedDuration::from_hours(1)).as_second(),
254 });
255 signed_jwt(KEY_ID, &claims)
256 }
257
258 fn request(authorization: Option<String>) -> Request<String> {
259 let mut builder = Request::builder().uri("http://orders.internal/orders");
260 if let Some(authorization) = authorization {
261 builder = builder.header(AUTHORIZATION, authorization);
262 }
263 builder.body(String::new()).unwrap()
264 }
265
266 #[derive(Clone)]
269 struct EchoClaims;
270
271 impl Service<Request<String>> for EchoClaims {
272 type Response = Response<String>;
273 type Error = Infallible;
274 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
275
276 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
277 Poll::Ready(Ok(()))
278 }
279
280 fn call(&mut self, req: Request<String>) -> Self::Future {
281 let claims = req.extensions().get::<VerifiedIdToken>().cloned();
282 Box::pin(async move {
283 let mut response = Response::new(String::new());
284 if let Some(claims) = claims {
285 response.extensions_mut().insert(claims);
286 }
287 Ok(response)
288 })
289 }
290 }
291
292 async fn send(layer: VerifyIdTokenLayer, req: Request<String>) -> Response<String> {
293 layer.layer(EchoClaims).call(req).await.unwrap()
294 }
295
296 #[tokio::test]
297 async fn missing_token_is_unauthorized() {
298 let response = send(
299 VerifyIdTokenLayer::new(verifier(TestKeys::Published)),
300 request(None),
301 )
302 .await;
303
304 assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
305 assert_eq!(
306 response.headers().get(WWW_AUTHENTICATE),
307 Some(&HeaderValue::from_static("Bearer"))
308 );
309 assert!(response.extensions().get::<VerifiedIdToken>().is_none());
310 }
311
312 #[tokio::test]
313 async fn token_for_another_audience_is_unauthorized() {
314 let token = token_for("https://billing-abc123-ew.a.run.app");
315
316 let response = send(
317 VerifyIdTokenLayer::new(verifier(TestKeys::Published)),
318 request(Some(format!("Bearer {token}"))),
319 )
320 .await;
321
322 assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
323 assert_eq!(
324 response.headers().get(WWW_AUTHENTICATE),
325 Some(&HeaderValue::from_static(r#"Bearer error="invalid_token""#))
326 );
327 assert!(response.extensions().get::<VerifiedIdToken>().is_none());
328 }
329
330 #[tokio::test]
331 async fn unavailable_keys_are_service_unavailable() {
332 let response = send(
333 VerifyIdTokenLayer::new(verifier(TestKeys::Unavailable)),
334 request(Some(format!("Bearer {}", token_for(AUDIENCE)))),
335 )
336 .await;
337
338 assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
339 assert!(response.extensions().get::<VerifiedIdToken>().is_none());
340 }
341
342 #[tokio::test]
343 async fn refused_principal_is_forbidden() {
344 let layer = VerifyIdTokenLayer::new(verifier(TestKeys::Published)).authorize(|token| {
345 token.verified_email()
346 == Some(&PrincipalEmail::new(
347 "billing@my-project.iam.gserviceaccount.com",
348 ))
349 });
350
351 let response = send(
352 layer,
353 request(Some(format!("Bearer {}", token_for(AUDIENCE)))),
354 )
355 .await;
356
357 assert_eq!(response.status(), StatusCode::FORBIDDEN);
358 assert!(response.extensions().get::<VerifiedIdToken>().is_none());
359 }
360
361 #[tokio::test]
362 async fn verified_claims_reach_the_inner_service() {
363 let layer = VerifyIdTokenLayer::new(verifier(TestKeys::Published))
364 .authorize(|token| token.verified_email() == Some(&PrincipalEmail::new(CALLER)));
365
366 let response = send(
367 layer,
368 request(Some(format!("bearer {}", token_for(AUDIENCE)))),
369 )
370 .await;
371
372 assert_eq!(response.status(), StatusCode::OK);
373 let claims = response.extensions().get::<VerifiedIdToken>().unwrap();
374 assert_eq!(claims.verified_email(), Some(&PrincipalEmail::new(CALLER)));
375 }
376
377 #[tokio::test]
378 async fn extractor_reads_the_verified_claims() {
379 let claims = verifier(TestKeys::Published)
380 .verify(&token_for(AUDIENCE))
381 .await
382 .unwrap();
383 let (mut parts, _) = request(None).into_parts();
384 parts.extensions.insert(claims);
385
386 let extracted = VerifiedIdToken::from_request_parts(&mut parts, &())
387 .await
388 .unwrap();
389
390 assert_eq!(
391 extracted.verified_email(),
392 Some(&PrincipalEmail::new(CALLER))
393 );
394 }
395
396 #[tokio::test]
397 async fn extractor_outside_the_layer_is_an_internal_error() {
398 let (mut parts, _) = request(None).into_parts();
399
400 let rejection = VerifiedIdToken::from_request_parts(&mut parts, &())
401 .await
402 .unwrap_err();
403
404 assert_eq!(rejection, StatusCode::INTERNAL_SERVER_ERROR);
405 }
406}