mockforge_http/auth/
middleware.rs1use axum::body::Body;
7use axum::http::{Request, StatusCode};
8use axum::{extract::State, middleware::Next, response::Response};
9use tracing::{debug, error, warn};
10
11use super::authenticator::authenticate_request;
12use super::state::AuthState;
13use super::types::AuthResult;
14
15pub async fn auth_middleware(
17 State(state): State<AuthState>,
18 req: Request<Body>,
19 next: Next,
20) -> Response {
21 let path = req.uri().path().to_string();
22 let _method = req.method().clone();
23
24 if path.starts_with("/health") || path.starts_with("/__mockforge") {
26 return next.run(req).await;
27 }
28
29 let auth_header = req
31 .headers()
32 .get("authorization")
33 .and_then(|h| h.to_str().ok())
34 .map(|s| s.to_string());
35
36 let api_key_header = req
37 .headers()
38 .get(
39 state
40 .config
41 .api_key
42 .as_ref()
43 .map(|c| c.header_name.clone())
44 .unwrap_or_else(|| "X-API-Key".to_string()),
45 )
46 .and_then(|h| h.to_str().ok())
47 .map(|s| s.to_string());
48
49 let api_key_query = req.uri().query().and_then(|q| {
50 state
51 .config
52 .api_key
53 .as_ref()
54 .and_then(|c| c.query_name.as_ref())
55 .and_then(|param| {
56 url::form_urlencoded::parse(q.as_bytes())
57 .find(|(k, _)| k == param)
58 .map(|(_, v)| v.to_string())
59 })
60 });
61
62 let auth_result =
64 authenticate_request(&state, &auth_header, &api_key_header, &api_key_query).await;
65
66 match auth_result {
67 AuthResult::Success(claims) => {
68 debug!("Authentication successful for user: {:?}", claims.sub);
69 let mut req = req;
71 req.extensions_mut().insert(claims);
72 next.run(req).await
73 }
74 AuthResult::Failure(reason) => {
75 warn!("Authentication failed: {}", reason);
76 let mut res = Response::new(axum::body::Body::from(
77 serde_json::json!({
78 "error": "Authentication failed",
79 "message": reason
80 })
81 .to_string(),
82 ));
83 *res.status_mut() = StatusCode::UNAUTHORIZED;
84 res.headers_mut().insert("www-authenticate", "Bearer".parse().unwrap());
85 res
86 }
87 AuthResult::NetworkError(reason) => {
88 error!("Authentication network error: {}", reason);
89 let mut res = Response::new(axum::body::Body::from(
90 serde_json::json!({
91 "error": "Authentication service unavailable",
92 "message": "Unable to verify token due to network issues"
93 })
94 .to_string(),
95 ));
96 *res.status_mut() = StatusCode::SERVICE_UNAVAILABLE;
97 res
98 }
99 AuthResult::ServerError(reason) => {
100 error!("Authentication server error: {}", reason);
101 let mut res = Response::new(axum::body::Body::from(
102 serde_json::json!({
103 "error": "Authentication service error",
104 "message": "Unable to verify token due to server issues"
105 })
106 .to_string(),
107 ));
108 *res.status_mut() = StatusCode::BAD_GATEWAY;
109 res
110 }
111 AuthResult::TokenExpired => {
112 warn!("Token expired");
113 let mut res = Response::new(axum::body::Body::from(
114 serde_json::json!({
115 "error": "Token expired",
116 "message": "The provided token has expired"
117 })
118 .to_string(),
119 ));
120 *res.status_mut() = StatusCode::UNAUTHORIZED;
121 res.headers_mut().insert(
122 "www-authenticate",
123 "Bearer error=\"invalid_token\", error_description=\"The token has expired\""
124 .parse()
125 .unwrap(),
126 );
127 res
128 }
129 AuthResult::TokenInvalid(reason) => {
130 warn!("Token invalid: {}", reason);
131 let mut res = Response::new(axum::body::Body::from(
132 serde_json::json!({
133 "error": "Invalid token",
134 "message": reason
135 })
136 .to_string(),
137 ));
138 *res.status_mut() = StatusCode::UNAUTHORIZED;
139 res.headers_mut()
140 .insert("www-authenticate", "Bearer error=\"invalid_token\"".parse().unwrap());
141 res
142 }
143 AuthResult::None => {
144 if state.config.require_auth {
145 let mut res = Response::new(axum::body::Body::from(
146 serde_json::json!({
147 "error": "Authentication required"
148 })
149 .to_string(),
150 ));
151 *res.status_mut() = StatusCode::UNAUTHORIZED;
152 res.headers_mut().insert("www-authenticate", "Bearer".parse().unwrap());
153 res
154 } else {
155 debug!("No authentication provided, proceeding without auth");
156 next.run(req).await
157 }
158 }
159 }
160}