Skip to main content

oxinbox_backend/
auth.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3
4use tokio::sync::RwLock;
5use tracing::instrument;
6use url::Url;
7use webauthn_rs::prelude::*;
8
9use crate::ai::AiProvider;
10use crate::database::ParadeDbRepository;
11use crate::push::PushService;
12
13#[derive(Clone)]
14pub struct AuthState {
15    pub webauthn: Arc<Webauthn>,
16    pub reg_states: Arc<RwLock<HashMap<String, (PasskeyRegistration, String)>>>,
17    pub auth_states: Arc<RwLock<HashMap<String, (PasskeyAuthentication, i32)>>>,
18    pub credentials: Arc<RwLock<HashMap<i32, Vec<Passkey>>>>,
19    pub users: Arc<RwLock<HashMap<String, i32>>>,
20    pub sessions: Arc<RwLock<HashMap<String, i32>>>,
21    pub ai_provider: Option<Arc<dyn AiProvider>>,
22    pub db: Option<Arc<ParadeDbRepository>>,
23    pub push: PushService,
24}
25
26impl AuthState {
27    #[instrument(skip(ai_provider, db, push))]
28    pub fn new(
29        ai_provider: Option<Arc<dyn AiProvider>>,
30        db: Option<Arc<ParadeDbRepository>>,
31        push: PushService,
32    ) -> Self {
33        let rp_origin_str =
34            std::env::var("RP_ORIGIN").unwrap_or_else(|_| "http://localhost:3300".into());
35        let rp_origin = Url::parse(&rp_origin_str).expect("invalid RP_ORIGIN");
36
37        let rp_id = std::env::var("RP_ID").unwrap_or_else(|_| {
38            rp_origin.host_str().expect("RP_ORIGIN must have a host").into()
39        });
40
41        tracing::debug!(%rp_id, %rp_origin_str, "configuring WebAuthn");
42
43        let webauthn = WebauthnBuilder::new(&rp_id, &rp_origin)
44            .expect("failed to create webauthn builder")
45            .build()
46            .expect("failed to build webauthn");
47
48        Self {
49            webauthn: Arc::new(webauthn),
50            reg_states: Arc::new(RwLock::new(HashMap::new())),
51            auth_states: Arc::new(RwLock::new(HashMap::new())),
52            credentials: Arc::new(RwLock::new(HashMap::new())),
53            users: Arc::new(RwLock::new(HashMap::new())),
54            sessions: Arc::new(RwLock::new(HashMap::new())),
55            ai_provider,
56            db,
57            push,
58        }
59    }
60
61    pub fn next_user_id() -> i32 {
62        use std::sync::atomic::{AtomicI32, Ordering};
63        static COUNTER: AtomicI32 = AtomicI32::new(1);
64        COUNTER.fetch_add(1, Ordering::Relaxed)
65    }
66
67    pub fn generate_token() -> String {
68        use rand::Rng;
69        let mut rng = rand::thread_rng();
70        (0..64)
71            .map(|_| rng.sample(rand::distributions::Alphanumeric) as char)
72            .collect()
73    }
74}
75
76#[derive(Clone, Copy, Debug)]
77pub struct AuthUser {
78    pub user_id: i32,
79}