use std::collections::HashMap;
use argon2::{
Argon2, PasswordHash,
password_hash::{PasswordHasher, SaltString},
};
use otpauth::TOTP;
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
use webauthn_rs::prelude::{Passkey, RequestChallengeResponse};
use crate::data::sane_name::SaneName;
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AuthData {
pub phc: Option<String>,
pub totp_setup: Option<String>,
pub webauthn: HashMap<SaneName, Passkey>,
}
impl AuthData {
pub fn password_hash(&self) -> Option<PasswordHash<'_>> {
Some(PasswordHash::new(self.phc.as_deref()?).expect("valid serialized data"))
}
pub fn set_password(
&mut self,
password: &str,
) -> Result<(), argon2::password_hash::errors::Error> {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
let hash = argon2.hash_password(password.as_bytes(), &salt)?;
self.phc = Some(hash.to_string());
Ok(())
}
pub fn totp(&self) -> Option<TOTP> {
TOTP::from_base32(self.totp_setup.as_ref()?)
}
pub fn len(&self) -> u8 {
self.phc.is_some() as u8 + self.totp_setup.is_some() as u8 + !self.webauthn.is_empty() as u8
}
pub fn is_empty(&self) -> bool {
self.phc.is_none() && self.totp_setup.is_none() && self.webauthn.is_empty()
}
}
#[non_exhaustive]
#[derive(Default)]
pub struct LoginChallenge {
pub password: bool,
pub totp: bool,
pub webauthn: Option<RequestChallengeResponse>,
}