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
39 .host_str()
40 .expect("RP_ORIGIN must have a host")
41 .into()
42 });
43
44 tracing::debug!(%rp_id, %rp_origin_str, "configuring WebAuthn");
45
46 let webauthn = WebauthnBuilder::new(&rp_id, &rp_origin)
47 .expect("failed to create webauthn builder")
48 .build()
49 .expect("failed to build webauthn");
50
51 Self {
52 webauthn: Arc::new(webauthn),
53 reg_states: Arc::new(RwLock::new(HashMap::new())),
54 auth_states: Arc::new(RwLock::new(HashMap::new())),
55 credentials: Arc::new(RwLock::new(HashMap::new())),
56 users: Arc::new(RwLock::new(HashMap::new())),
57 sessions: Arc::new(RwLock::new(HashMap::new())),
58 ai_provider,
59 db,
60 push,
61 }
62 }
63
64 pub fn next_user_id() -> i32 {
65 use std::sync::atomic::{AtomicI32, Ordering};
66 static COUNTER: AtomicI32 = AtomicI32::new(1);
67 COUNTER.fetch_add(1, Ordering::Relaxed)
68 }
69
70 pub fn generate_token() -> String {
71 use rand::Rng;
72 let mut rng = rand::thread_rng();
73 (0..64)
74 .map(|_| rng.sample(rand::distributions::Alphanumeric) as char)
75 .collect()
76 }
77}
78
79#[derive(Clone, Copy, Debug)]
80pub struct AuthUser {
81 pub user_id: i32,
82}