use std::sync::LazyLock; use argon2::{Argon2, PasswordHasher, password_hash::SaltString}; use axum::http::HeaderMap; use rand08::{RngCore, rngs::OsRng}; use crate::users::{ User, UserError, sessions::{Session, SessionError}, }; pub mod implementation; pub const COOKIE_NAME: &str = "mnemohash"; pub trait UserAuthenticate { fn authenticate(headers: &HeaderMap) -> Result, AuthError>; } pub trait UserAuthRequired { fn required(self) -> Result; } pub trait SessionAuthenticate { fn authenticate(headers: &HeaderMap) -> Result, AuthError>; } pub trait SessionAuthRequired { fn required(self) -> Result; } pub trait UserPasswordHashing { /// Returns the hashed password as a String fn hash_password(passw: &str) -> Result; /// Returns whether the password matches the hash fn match_hash_password(passw: &str, hash: &str) -> Result; } pub static SHARED_ARGON: LazyLock = LazyLock::new(|| Argon2::default()); pub const DUMMY_PASSWORD: &str = "PASSWORD"; pub static DUMMY_PASSWORD_PHC: LazyLock = LazyLock::new(|| { let salt = SaltString::generate(&mut OsRng); SHARED_ARGON .hash_password(DUMMY_PASSWORD.as_bytes(), &salt) .unwrap() .to_string() }); pub fn init_password_dummies() { let _ = &*DUMMY_PASSWORD_PHC; println!("Password hashing setup finished"); } #[derive(thiserror::Error, Debug)] pub enum AuthError { #[error("Invalid credentials")] InvalidCredentials, #[error("Authentication required")] AuthRequired, #[error("Session error: {0}")] SessionError(#[from] SessionError), #[error("User error: {0}")] UserError(#[from] UserError), #[error("Invalid authorization header format")] InvalidFormat, #[error("Invalid base64 encoding")] InvalidBase64(#[from] base64::DecodeError), #[error("Invalid UTF-8 in credentials")] InvalidUtf8(#[from] std::string::FromUtf8Error), #[error("Database error: {0}")] DatabaseError(#[from] rusqlite::Error), #[error("Argon2 passhash error: {0}")] PassHashError(argon2::password_hash::Error), } #[derive(Debug, Clone, Copy)] #[allow(unused)] pub enum TokenSize { /// 5 bytes = 8 chars Char8, /// 10 bytes = 16 chars Char16, /// 20 bytes = 32 chars Char32, /// 40 bytes = 64 chars Char64, } pub fn generate_token(len: TokenSize) -> String { let mut bytes = vec![0u8; len.bytes()]; let mut rng = OsRng; rng.try_fill_bytes(&mut bytes).unwrap(); base32::encode(base32::Alphabet::Crockford, &bytes) }