use std::sync::Arc; use rand::{seq::IteratorRandom, thread_rng}; use thiserror::Error; use tokio::sync::Mutex; use tracing::{event, instrument, Level}; use crate::{ db::{/* Database, */ Database, SqliteDatabase}, secrets::{Password, UserToken}, }; #[derive(Debug, Error)] pub enum AuthenticationError { #[error("Invalid username or password")] InvalidUserOrPassword, #[error("Invalid token")] InvalidToken, #[error("Invalid server hash")] InvalidServerHash, #[error("Authentication backend error")] Backend(#[from] sqlx::Error), } #[derive(Debug)] pub struct Authenticator { db: Arc>, } impl Authenticator { pub fn new(db: Arc>) -> Self { Self { db } } #[instrument] pub async fn create_user_token(&mut self, username: &str, password: &Password) -> UserToken { let mut token_str = "".to_owned(); let mut rng = thread_rng(); for _ in 0..30 { let digit = (0..=15).choose(&mut rng).unwrap(); let char = (('a' as u8) + digit) as char; token_str.push(char) } let new_token = UserToken::from(token_str); let mut db = self.db.lock().await; if let Err(err) = db.save_token(username, &new_token).await { event!(Level::ERROR, %err, "Failed to save token in database"); } new_token } #[instrument] pub async fn verify_user_token( &self, username: &str, token: &UserToken, ) -> Result<(), AuthenticationError> { let mut db = self.db.lock().await; // TODO: (in db) distinguish between invalid token and SQLX error db.get_token(username).await?; Ok(()) } }