fedimint_server_ui/
auth.rs

1use axum::extract::FromRequestParts;
2use axum::http::request::Parts;
3use axum::response::Redirect;
4use axum_extra::extract::CookieJar;
5use fedimint_server_core::net::GuardianAuthToken;
6
7use crate::{LOGIN_ROUTE, UiState};
8
9/// Extractor that validates user authentication
10pub struct UserAuth {
11    /// UserAuth is an axum extractor guaranteeing when the admin password was
12    /// verified. This implies we can grant logic holding it access to
13    /// fedimint-core internals that require `GuardianAuthToken`, which is a
14    /// very similar mechanism.
15    pub guardian_auth_token: GuardianAuthToken,
16}
17
18impl UserAuth {
19    fn authenticated() -> Self {
20        Self {
21            guardian_auth_token: GuardianAuthToken::new_unchecked(),
22        }
23    }
24}
25
26impl<Api> FromRequestParts<UiState<Api>> for UserAuth
27where
28    Api: Send + Sync + 'static,
29{
30    type Rejection = Redirect;
31
32    async fn from_request_parts(
33        parts: &mut Parts,
34        state: &UiState<Api>,
35    ) -> Result<Self, Self::Rejection> {
36        let jar = CookieJar::from_request_parts(parts, state)
37            .await
38            .map_err(|_| Redirect::to(LOGIN_ROUTE))?;
39
40        // Check if the auth cookie exists and has the correct value
41        match jar.get(&state.auth_cookie_name) {
42            Some(cookie) if cookie.value() == state.auth_cookie_value => {
43                Ok(UserAuth::authenticated())
44            }
45            _ => Err(Redirect::to(LOGIN_ROUTE)),
46        }
47    }
48}