Skip to main content

freshblu_server/handlers/
mod.rs

1pub mod auth;
2pub mod devices;
3pub mod messages;
4pub mod status;
5pub mod subscriptions;
6pub mod tokens;
7
8use axum::{
9    extract::FromRequestParts,
10    http::request::Parts,
11    response::{IntoResponse, Response},
12};
13use freshblu_core::{auth::parse_basic_auth, device::Device, error::FreshBluError};
14use uuid::Uuid;
15
16use crate::{ApiError, AppState};
17
18/// Extractor: authenticated device from HTTP Basic Auth header (uuid:token)
19pub struct AuthenticatedDevice(pub Device, pub Option<Uuid>);
20
21#[axum::async_trait]
22impl FromRequestParts<AppState> for AuthenticatedDevice {
23    type Rejection = Response;
24
25    async fn from_request_parts(
26        parts: &mut Parts,
27        state: &AppState,
28    ) -> Result<Self, Self::Rejection> {
29        let headers = &parts.headers;
30
31        // Try Authorization header first
32        let creds = headers
33            .get("authorization")
34            .and_then(|v| v.to_str().ok())
35            .and_then(parse_basic_auth);
36
37        // Fallback: skynet_auth header (legacy)
38        let creds = creds.or_else(|| {
39            headers
40                .get("skynet_auth_uuid")
41                .zip(headers.get("skynet_auth_token"))
42                .and_then(|(u, t)| {
43                    Some((
44                        u.to_str().ok()?.to_string(),
45                        t.to_str().ok()?.to_string(),
46                    ))
47                })
48        });
49
50        let (uuid_str, token) = creds.ok_or_else(|| {
51            ApiError::from(FreshBluError::Unauthorized).into_response()
52        })?;
53
54        let uuid = Uuid::parse_str(&uuid_str).map_err(|_| {
55            ApiError::from(FreshBluError::Unauthorized).into_response()
56        })?;
57
58        let device = state
59            .store
60            .authenticate(&uuid, &token)
61            .await
62            .map_err(|e| ApiError::from(e).into_response())?
63            .ok_or_else(|| ApiError::from(FreshBluError::Unauthorized).into_response())?;
64
65        // Check x-meshblu-as header for acting as another device
66        let as_uuid = headers
67            .get("x-meshblu-as")
68            .and_then(|v| v.to_str().ok())
69            .and_then(|v| Uuid::parse_str(v).ok());
70
71        Ok(AuthenticatedDevice(device, as_uuid))
72    }
73}