fbc_starter/auth/
context.rs1use axum::{
2 extract::{FromRequestParts, OptionalFromRequestParts, Request},
3 http::{request::Parts, StatusCode},
4 middleware::Next,
5 response::{IntoResponse, Response},
6};
7
8#[derive(Debug, Clone)]
15pub struct RequestContext {
16 pub user_id: i64,
18 pub tenant_id: Option<i64>,
20 pub username: String,
22}
23
24pub async fn user_context_middleware(request: Request, next: Next) -> Response {
32 let mut request = request;
33
34 let user_id = request
36 .headers()
37 .get("X-User-Id")
38 .and_then(|v| v.to_str().ok())
39 .and_then(|s| s.parse::<i64>().ok());
40
41 if let Some(user_id) = user_id {
42 let tenant_id = request
43 .headers()
44 .get("X-Tenant-Id")
45 .and_then(|v| v.to_str().ok())
46 .and_then(|s| s.parse::<i64>().ok());
47
48 let username = request
49 .headers()
50 .get("X-Username")
51 .and_then(|v| v.to_str().ok())
52 .unwrap_or("")
53 .to_string();
54
55 let ctx = RequestContext {
56 user_id,
57 tenant_id,
58 username,
59 };
60
61 request.extensions_mut().insert(ctx);
62 }
63
64 next.run(request).await
65}
66
67impl<S> FromRequestParts<S> for RequestContext
78where
79 S: Send + Sync,
80{
81 type Rejection = Response;
82
83 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
84 parts
85 .extensions
86 .get::<RequestContext>()
87 .cloned()
88 .ok_or_else(|| (StatusCode::UNAUTHORIZED, "Missing user context").into_response())
89 }
90}
91
92impl<S> OptionalFromRequestParts<S> for RequestContext
106where
107 S: Send + Sync,
108{
109 type Rejection = std::convert::Infallible;
110
111 async fn from_request_parts(
112 parts: &mut Parts,
113 _state: &S,
114 ) -> Result<Option<Self>, Self::Rejection> {
115 Ok(parts.extensions.get::<RequestContext>().cloned())
116 }
117}
118