snap_control/server/
auth.rs1use std::{
17 fmt::Display,
18 future::Future,
19 pin::Pin,
20 sync::Arc,
21 task::{Context, Poll},
22};
23
24use axum::body::Body;
25use http::{Request, Response};
26use thiserror::Error;
27use tower::{Layer, Service};
28
29use crate::server::token_verifier::SnapTokenVerifier;
30
31#[derive(Clone)]
32pub(crate) struct AuthMiddlewareLayer {
33 verifier: Arc<SnapTokenVerifier>,
34}
35
36impl AuthMiddlewareLayer {
37 pub(crate) fn new(verifier: SnapTokenVerifier) -> Self {
38 Self {
39 verifier: Arc::new(verifier),
40 }
41 }
42}
43
44impl<S> Layer<S> for AuthMiddlewareLayer {
45 type Service = AuthMiddleware<S>;
46
47 fn layer(&self, inner: S) -> Self::Service {
48 AuthMiddleware::new(inner, self.verifier.clone())
49 }
50}
51
52#[derive(Clone)]
53pub(crate) struct AuthMiddleware<S> {
54 inner: S,
55 verifier: Arc<SnapTokenVerifier>,
56}
57
58impl<S> AuthMiddleware<S> {
59 pub(crate) fn new(inner: S, verifier: Arc<SnapTokenVerifier>) -> Self {
60 Self { inner, verifier }
61 }
62}
63
64impl<S> Service<Request<Body>> for AuthMiddleware<S>
65where
66 S: Service<Request<Body>, Response = Response<Body>> + Send + Clone + 'static,
67 S::Future: Send + 'static,
68{
69 type Response = Response<Body>;
70 type Error = S::Error;
71 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
72
73 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
74 self.inner.poll_ready(cx)
75 }
76
77 fn call(&mut self, mut request: Request<Body>) -> Self::Future {
78 let token = match extract_bearer_token(&request) {
79 Ok(token) => token,
80 Err(err) => {
81 tracing::debug!(%err, "Extract bearer token");
82 return Box::pin(async { Ok(build_unauthorized_response(err)) });
83 }
84 };
85
86 let verifier = self.verifier.clone();
87 let mut inner = self.inner.clone();
88 Box::pin(async move {
89 match verifier.verify(&token).await {
90 Ok(token_claims) => {
91 request.extensions_mut().insert(token_claims);
92 inner.call(request).await
93 }
94 Err(err) => {
95 tracing::debug!(%err, "Invalid Token");
96 Ok(build_unauthorized_response(err))
97 }
98 }
99 })
100 }
101}
102
103fn build_unauthorized_response<E: Display>(err: E) -> Response<Body> {
104 Response::builder()
105 .status(http::StatusCode::UNAUTHORIZED)
106 .body(Body::from(format!("SNAP Token validation failed: {err}")))
107 .expect("no fail")
108}
109
110pub fn extract_bearer_token(req: &Request<Body>) -> Result<String, ExtractBearerTokenError> {
112 let auth_header = match req.headers().get("authorization") {
113 Some(header) => header,
114 None => return Err(ExtractBearerTokenError::AuthHeaderMissing),
115 };
116
117 let auth_str = match auth_header.to_str() {
118 Ok(str) => str,
119 Err(_) => return Err(ExtractBearerTokenError::AuthHeaderInvalidUtf8),
120 };
121
122 match auth_str.strip_prefix("Bearer ") {
123 Some(token) => Ok(token.to_string()),
124 None => Err(ExtractBearerTokenError::AuthHeaderNotBearer),
125 }
126}
127
128#[derive(Debug, Error)]
130pub enum ExtractBearerTokenError {
131 #[error("authorization header is missing")]
133 AuthHeaderMissing,
134 #[error("authorization header is not valid UTF-8")]
136 AuthHeaderInvalidUtf8,
137 #[error("authorization header is not a bearer token")]
139 AuthHeaderNotBearer,
140}