Skip to main content

doido_auth/
layer.rs

1//! Axum middleware that resolves auth identity via enabled strategies.
2
3use crate::error::AuthError;
4use crate::identity::AuthIdentity;
5use crate::state::global;
6use axum::body::Body;
7use axum::http::Request;
8use axum::middleware::Next;
9use axum::response::Response;
10
11/// Tower/axum layer function: consults strategies in config order and stores the
12/// first resolved [`AuthIdentity`] in request extensions.
13pub async fn auth_layer(req: Request<Body>, next: Next) -> Response {
14    let state = match crate::state::try_global() {
15        Some(s) => s,
16        None => return next.run(req).await,
17    };
18
19    let (mut parts, body) = req.into_parts();
20    for strategy in &state.strategies {
21        match strategy.authenticate(&parts, &state.db).await {
22            Ok(Some(identity)) => {
23                parts.extensions.insert(identity);
24                break;
25            }
26            Ok(None) => {}
27            Err(_) => {}
28        }
29    }
30    next.run(Request::from_parts(parts, body)).await
31}
32
33/// Read the authenticated identity from request extensions.
34pub fn current_identity(parts: &http::request::Parts) -> Option<AuthIdentity> {
35    crate::strategy::identity_from_parts(parts)
36}
37
38/// Load the current user model from extensions + DB.
39pub async fn current_user<U: crate::user::AuthUser>(
40    parts: &http::request::Parts,
41) -> Result<U, AuthError> {
42    let identity = crate::strategy::require_identity(parts)?;
43    let id: U::Id = serde_json::from_value(identity.user_id.clone())
44        .map_err(|e| AuthError::Internal(e.to_string()))?;
45    U::find_by_id(&global().db, id)
46        .await
47        .map_err(|e| AuthError::Internal(e.to_string()))?
48        .ok_or(AuthError::Unauthorized)
49}