- auth: register/login/refresh/logout w/ JWT+Argon2, protected mutations - domain: User, RefreshSession, auth ports, Unauthorized/Forbidden errors - presentation: context/state/factory/errors/extractors/openapi modules - routes behind /api, SPA served from root w/ fallback - OpenAPI Scalar at /docs - frontend ssr:false, single-binary Dockerfile
35 lines
1.5 KiB
Rust
35 lines
1.5 KiB
Rust
use async_trait::async_trait;
|
|
|
|
use crate::errors::DomainError;
|
|
use crate::models::{GeneratedToken, RefreshSession, User};
|
|
use crate::value_objects::{Email, PasswordHash, UserId, Username};
|
|
|
|
#[async_trait]
|
|
pub trait AuthService: Send + Sync {
|
|
async fn generate_token(&self, user_id: &UserId) -> Result<GeneratedToken, DomainError>;
|
|
async fn validate_token(&self, token: &str) -> Result<UserId, DomainError>;
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait PasswordHasher: Send + Sync {
|
|
async fn hash(&self, plain_password: &str) -> Result<PasswordHash, DomainError>;
|
|
async fn verify(&self, plain_password: &str, hash: &PasswordHash) -> Result<bool, DomainError>;
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait UserRepository: Send + Sync {
|
|
async fn find_by_email(&self, email: &Email) -> Result<Option<User>, DomainError>;
|
|
async fn find_by_username(&self, username: &Username) -> Result<Option<User>, DomainError>;
|
|
async fn find_by_id(&self, id: &UserId) -> Result<Option<User>, DomainError>;
|
|
async fn save(&self, user: &User) -> Result<(), DomainError>;
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait RefreshSessionRepository: Send + Sync {
|
|
async fn create(&self, session: &RefreshSession) -> Result<(), DomainError>;
|
|
async fn get_by_token(&self, token: &str) -> Result<Option<RefreshSession>, DomainError>;
|
|
async fn revoke(&self, token: &str) -> Result<(), DomainError>;
|
|
async fn revoke_all_for_user(&self, user_id: &UserId) -> Result<(), DomainError>;
|
|
async fn delete_expired(&self) -> Result<u64, DomainError>;
|
|
}
|