use crate::{ db::{self, Data}, prelude::*, user::{self, User}, }; use axum::async_trait; use axum_login::{AuthnBackend, UserId}; use redact::Secret; use serde::Deserialize; use thiserror::Error; #[derive(Clone)] pub struct State { pub db: Data, } impl State { pub async fn try_new() -> Result { Ok(Self { db: Data::try_new()?, }) } } #[derive(Error, Debug)] pub enum NewStateError { #[error("database error: {0}")] Database(#[from] db::Error), } #[derive(Deserialize, Debug, Clone)] pub struct Creds { pub username: String, pub password: Secret, } #[derive(Error, Debug)] pub enum AuthError { #[error("user error: {0}")] User(#[from] user::Error), #[error("data error: {0}")] Db(#[from] db::Error), #[error("data error: {0}")] Argon2(#[from] argon2::password_hash::Error), } #[async_trait] impl AuthnBackend for State { type User = User; type Credentials = Creds; type Error = AuthError; async fn authenticate( &self, Creds { username, password }: Self::Credentials, ) -> Result, Self::Error> { if let Some(user) = self.db.get::(User::tree(), username)? { if let Err(err) = user.verify(password.expose_secret()) { Err(err.into()) } else { Ok(Some(user)) } } else { Ok(None) } } async fn get_user(&self, username: &UserId) -> Result, Self::Error> { match self.db.get::<&str, User>(User::tree(), username)? { Some(user) => Ok(Some(user)), None => Ok(None), } } }