mellon 0.1.0

Library for adding contemporary authentication to rust-based websites.
Documentation
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;

/// Presistent auth data (stored on disk)
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct AuthData {
    /// hash in Password Hashing Competition format (PHC)
    ///
    /// Use the `.password_hash()` method to work with it.
    pub phc: Option<String>,
    /// TOTP secret as base32 encoded data
    ///
    /// Use the `.totp()` method to work with it.
    pub totp_setup: Option<String>,
    /// Webauthn data
    ///
    /// It is recommend to have several passkeys registered in case one gets lost.
    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()?)
    }

    /// Return the number auf active authentication methods
    pub fn len(&self) -> u8 {
        self.phc.is_some() as u8 + self.totp_setup.is_some() as u8 + !self.webauthn.is_empty() as u8
    }

    /// Are there currently no authentication methods associated with this account?
    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>,
}