feat: Centralize axum-login authentication logic in infra and introduce configurable database and session settings.
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -39,14 +39,12 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum",
|
"axum",
|
||||||
"axum-login",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"config",
|
"config",
|
||||||
"domain",
|
"domain",
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"infra",
|
"infra",
|
||||||
"k-core",
|
"k-core",
|
||||||
"password-auth",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
@@ -54,7 +52,6 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-sessions",
|
|
||||||
"tower-sessions-sqlx-store",
|
"tower-sessions-sqlx-store",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -1175,18 +1172,22 @@ dependencies = [
|
|||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"async-nats",
|
"async-nats",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"axum-login",
|
||||||
"chrono",
|
"chrono",
|
||||||
"domain",
|
"domain",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"k-core",
|
"k-core",
|
||||||
|
"password-auth",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tower-sessions",
|
||||||
"tower-sessions-sqlx-store",
|
"tower-sessions-sqlx-store",
|
||||||
"tracing",
|
"tracing",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
|||||||
@@ -5,15 +5,10 @@ edition = "2024"
|
|||||||
default-run = "api"
|
default-run = "api"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sqlite"]
|
default = ["sqlite", "auth-axum-login"]
|
||||||
sqlite = [
|
sqlite = ["infra/sqlite", "tower-sessions-sqlx-store/sqlite"]
|
||||||
"infra/sqlite",
|
postgres = ["infra/postgres", "tower-sessions-sqlx-store/postgres"]
|
||||||
"tower-sessions-sqlx-store/sqlite",
|
auth-axum-login = ["infra/auth-axum-login"]
|
||||||
]
|
|
||||||
postgres = [
|
|
||||||
"infra/postgres",
|
|
||||||
"tower-sessions-sqlx-store/postgres",
|
|
||||||
]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
||||||
@@ -21,12 +16,11 @@ k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features
|
|||||||
"db-sqlx",
|
"db-sqlx",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
"http",
|
"http",
|
||||||
"auth","sessions-db"
|
"auth",
|
||||||
|
"sessions-db",
|
||||||
] }
|
] }
|
||||||
domain = { path = "../domain" }
|
domain = { path = "../domain" }
|
||||||
infra = { path = "../infra", default-features = false, features = [
|
infra = { path = "../infra", default-features = false, features = ["sqlite"] }
|
||||||
"sqlite",
|
|
||||||
] }
|
|
||||||
|
|
||||||
#Web framework
|
#Web framework
|
||||||
axum = { version = "0.8.8", features = ["macros"] }
|
axum = { version = "0.8.8", features = ["macros"] }
|
||||||
@@ -34,10 +28,9 @@ tower = "0.5.2"
|
|||||||
tower-http = { version = "0.6.2", features = ["cors", "trace"] }
|
tower-http = { version = "0.6.2", features = ["cors", "trace"] }
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
axum-login = "0.18"
|
# Moved to infra
|
||||||
tower-sessions = "0.14"
|
|
||||||
tower-sessions-sqlx-store = { version = "0.15", features = ["sqlite"] }
|
tower-sessions-sqlx-store = { version = "0.15", features = ["sqlite"] }
|
||||||
password-auth = "1.0"
|
# password-auth removed
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
|
|
||||||
@@ -65,4 +58,3 @@ tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
|
|||||||
|
|
||||||
dotenvy = "0.15.7"
|
dotenvy = "0.15.7"
|
||||||
config = "0.15.19"
|
config = "0.15.19"
|
||||||
|
|
||||||
|
|||||||
102
api/src/auth.rs
102
api/src/auth.rs
@@ -1,101 +1,23 @@
|
|||||||
//! Authentication logic using axum-login
|
//! Authentication logic
|
||||||
|
//!
|
||||||
|
//! Proxies to infra implementation if enabled.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum_login::{AuthnBackend, UserId};
|
use domain::UserRepository;
|
||||||
use infra::session_store::InfraSessionStore;
|
use infra::session_store::{InfraSessionStore, SessionManagerLayer};
|
||||||
use password_auth::verify_password;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tower_sessions::SessionManagerLayer;
|
|
||||||
use uuid::Uuid;
|
|
||||||
|
|
||||||
use crate::error::ApiError;
|
use crate::error::ApiError;
|
||||||
use domain::{User, UserRepository};
|
|
||||||
|
|
||||||
/// Wrapper around domain User to implement AuthUser
|
#[cfg(feature = "auth-axum-login")]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
pub use infra::auth::backend::{AuthManagerLayer, AuthSession, AuthUser, Credentials};
|
||||||
pub struct AuthUser(pub User);
|
|
||||||
|
|
||||||
impl axum_login::AuthUser for AuthUser {
|
|
||||||
type Id = Uuid;
|
|
||||||
|
|
||||||
fn id(&self) -> Self::Id {
|
|
||||||
self.0.id
|
|
||||||
}
|
|
||||||
|
|
||||||
fn session_auth_hash(&self) -> &[u8] {
|
|
||||||
// Use password hash to invalidate sessions if password changes
|
|
||||||
self.0
|
|
||||||
.password_hash
|
|
||||||
.as_ref()
|
|
||||||
.map(|s| s.as_bytes())
|
|
||||||
.unwrap_or(&[])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct AuthBackend {
|
|
||||||
pub user_repo: Arc<dyn UserRepository>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuthBackend {
|
|
||||||
pub fn new(user_repo: Arc<dyn UserRepository>) -> Self {
|
|
||||||
Self { user_repo }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
pub struct Credentials {
|
|
||||||
pub email: String,
|
|
||||||
pub password: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AuthnBackend for AuthBackend {
|
|
||||||
type User = AuthUser;
|
|
||||||
type Credentials = Credentials;
|
|
||||||
type Error = ApiError;
|
|
||||||
|
|
||||||
async fn authenticate(
|
|
||||||
&self,
|
|
||||||
creds: Self::Credentials,
|
|
||||||
) -> Result<Option<Self::User>, Self::Error> {
|
|
||||||
let user = self
|
|
||||||
.user_repo
|
|
||||||
.find_by_email(&creds.email)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::internal(e.to_string()))?;
|
|
||||||
|
|
||||||
if let Some(user) = user {
|
|
||||||
if let Some(hash) = &user.password_hash {
|
|
||||||
// Verify password
|
|
||||||
if verify_password(&creds.password, hash).is_ok() {
|
|
||||||
return Ok(Some(AuthUser(user)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_user(&self, user_id: &UserId<Self>) -> Result<Option<Self::User>, Self::Error> {
|
|
||||||
let user = self
|
|
||||||
.user_repo
|
|
||||||
.find_by_id(*user_id)
|
|
||||||
.await
|
|
||||||
.map_err(|e| ApiError::internal(e.to_string()))?;
|
|
||||||
|
|
||||||
Ok(user.map(AuthUser))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type AuthSession = axum_login::AuthSession<AuthBackend>;
|
|
||||||
|
|
||||||
|
#[cfg(feature = "auth-axum-login")]
|
||||||
pub async fn setup_auth_layer(
|
pub async fn setup_auth_layer(
|
||||||
session_layer: SessionManagerLayer<InfraSessionStore>,
|
session_layer: SessionManagerLayer<InfraSessionStore>,
|
||||||
user_repo: Arc<dyn UserRepository>,
|
user_repo: Arc<dyn UserRepository>,
|
||||||
) -> Result<axum_login::AuthManagerLayer<AuthBackend, InfraSessionStore>, ApiError> {
|
) -> Result<AuthManagerLayer, ApiError> {
|
||||||
let backend = AuthBackend::new(user_repo);
|
infra::auth::backend::setup_auth_layer(session_layer, user_repo)
|
||||||
|
.await
|
||||||
let auth_layer = axum_login::AuthManagerLayerBuilder::new(backend, session_layer).build();
|
.map_err(|e| ApiError::Internal(e.to_string()))
|
||||||
Ok(auth_layer)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,27 @@ pub struct Config {
|
|||||||
|
|
||||||
#[serde(default = "default_host")]
|
#[serde(default = "default_host")]
|
||||||
pub host: String,
|
pub host: String,
|
||||||
|
|
||||||
|
#[serde(default = "default_secure_cookie")]
|
||||||
|
pub secure_cookie: bool,
|
||||||
|
|
||||||
|
#[serde(default = "default_db_max_connections")]
|
||||||
|
pub db_max_connections: u32,
|
||||||
|
|
||||||
|
#[serde(default = "default_db_min_connections")]
|
||||||
|
pub db_min_connections: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_secure_cookie() -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_db_max_connections() -> u32 {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_db_min_connections() -> u32 {
|
||||||
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_port() -> u16 {
|
fn default_port() -> u16 {
|
||||||
@@ -62,12 +83,30 @@ impl Config {
|
|||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let secure_cookie = env::var("SECURE_COOKIE")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
let db_max_connections = env::var("DB_MAX_CONNECTIONS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(5);
|
||||||
|
|
||||||
|
let db_min_connections = env::var("DB_MIN_CONNECTIONS")
|
||||||
|
.ok()
|
||||||
|
.and_then(|s| s.parse().ok())
|
||||||
|
.unwrap_or(1);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
database_url,
|
database_url,
|
||||||
session_secret,
|
session_secret,
|
||||||
cors_allowed_origins,
|
cors_allowed_origins,
|
||||||
|
secure_cookie,
|
||||||
|
db_max_connections,
|
||||||
|
db_min_connections,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ use domain::UserService;
|
|||||||
use infra::factory::build_session_store;
|
use infra::factory::build_session_store;
|
||||||
use infra::factory::build_user_repository;
|
use infra::factory::build_user_repository;
|
||||||
use infra::run_migrations;
|
use infra::run_migrations;
|
||||||
|
use infra::session_store::{Expiry, SessionManagerLayer};
|
||||||
use k_core::http::server::ServerConfig;
|
use k_core::http::server::ServerConfig;
|
||||||
use k_core::http::server::apply_standard_middleware;
|
use k_core::http::server::apply_standard_middleware;
|
||||||
use k_core::logging;
|
use k_core::logging;
|
||||||
use time::Duration;
|
use time::Duration;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tower_sessions::{Expiry, SessionManagerLayer};
|
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
mod auth;
|
mod auth;
|
||||||
@@ -37,8 +37,8 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
tracing::info!("Connecting to database: {}", config.database_url);
|
tracing::info!("Connecting to database: {}", config.database_url);
|
||||||
let db_config = k_core::db::DatabaseConfig {
|
let db_config = k_core::db::DatabaseConfig {
|
||||||
url: config.database_url.clone(),
|
url: config.database_url.clone(),
|
||||||
max_connections: 5,
|
max_connections: config.db_max_connections,
|
||||||
min_connections: 1,
|
min_connections: config.db_min_connections,
|
||||||
acquire_timeout: StdDuration::from_secs(30),
|
acquire_timeout: StdDuration::from_secs(30),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.map_err(|e| anyhow::anyhow!(e))?;
|
.map_err(|e| anyhow::anyhow!(e))?;
|
||||||
|
|
||||||
let session_layer = SessionManagerLayer::new(session_store)
|
let session_layer = SessionManagerLayer::new(session_store)
|
||||||
.with_secure(false) // Set to true in prod
|
.with_secure(config.secure_cookie)
|
||||||
.with_expiry(Expiry::OnInactivity(Duration::days(7)));
|
.with_expiry(Expiry::OnInactivity(Duration::days(7)));
|
||||||
|
|
||||||
let auth_layer = setup_auth_layer(session_layer, user_repo).await?;
|
let auth_layer = setup_auth_layer(session_layer, user_repo).await?;
|
||||||
|
|||||||
@@ -31,10 +31,10 @@ async fn login(
|
|||||||
password: payload.password,
|
password: payload.password,
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
.map_err(|e| ApiError::Internal(e.to_string()))?
|
||||||
{
|
{
|
||||||
Ok(Some(user)) => user,
|
Some(user) => user,
|
||||||
Ok(None) => return Err(ApiError::Validation("Invalid credentials".to_string())),
|
None => return Err(ApiError::Validation("Invalid credentials".to_string())),
|
||||||
Err(_) => return Err(ApiError::Internal("Authentication failed".to_string())),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auth_session
|
auth_session
|
||||||
|
|||||||
@@ -5,15 +5,26 @@ edition = "2024"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["sqlite", "broker-nats"]
|
default = ["sqlite", "broker-nats"]
|
||||||
sqlite = ["sqlx/sqlite", "k-core/sqlite", "tower-sessions-sqlx-store", "k-core/sessions-db"]
|
sqlite = [
|
||||||
postgres = ["sqlx/postgres", "k-core/postgres", "tower-sessions-sqlx-store", "k-core/sessions-db"]
|
"sqlx/sqlite",
|
||||||
|
"k-core/sqlite",
|
||||||
|
"tower-sessions-sqlx-store",
|
||||||
|
"k-core/sessions-db",
|
||||||
|
]
|
||||||
|
postgres = [
|
||||||
|
"sqlx/postgres",
|
||||||
|
"k-core/postgres",
|
||||||
|
"tower-sessions-sqlx-store",
|
||||||
|
"k-core/sessions-db",
|
||||||
|
]
|
||||||
broker-nats = ["dep:futures-util", "k-core/broker-nats"]
|
broker-nats = ["dep:futures-util", "k-core/broker-nats"]
|
||||||
|
auth-axum-login = ["dep:axum-login", "dep:password-auth"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
k-core = { git = "https://git.gabrielkaszewski.dev/GKaszewski/k-core", features = [
|
||||||
"logging",
|
"logging",
|
||||||
"db-sqlx",
|
"db-sqlx",
|
||||||
"sessions-db"
|
"sessions-db",
|
||||||
] }
|
] }
|
||||||
domain = { path = "../domain" }
|
domain = { path = "../domain" }
|
||||||
|
|
||||||
@@ -21,12 +32,18 @@ async-trait = "0.1.89"
|
|||||||
chrono = { version = "0.4.42", features = ["serde"] }
|
chrono = { version = "0.4.42", features = ["serde"] }
|
||||||
sqlx = { version = "0.8.6", features = ["runtime-tokio", "chrono", "migrate"] }
|
sqlx = { version = "0.8.6", features = ["runtime-tokio", "chrono", "migrate"] }
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
|
anyhow = "1.0"
|
||||||
tokio = { version = "1.48.0", features = ["full"] }
|
tokio = { version = "1.48.0", features = ["full"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
uuid = { version = "1.19.0", features = ["v4", "serde"] }
|
uuid = { version = "1.19.0", features = ["v4", "serde"] }
|
||||||
tower-sessions-sqlx-store = { version = "0.15.0", optional = true}
|
tower-sessions-sqlx-store = { version = "0.15.0", optional = true }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
async-nats = { version = "0.45", optional = true }
|
async-nats = { version = "0.45", optional = true }
|
||||||
futures-util = { version = "0.3", optional = true }
|
futures-util = { version = "0.3", optional = true }
|
||||||
futures-core = "0.3"
|
futures-core = "0.3"
|
||||||
|
tower-sessions = "0.14"
|
||||||
|
|
||||||
|
# Auth dependencies (optional)
|
||||||
|
axum-login = { version = "0.18", optional = true }
|
||||||
|
password-auth = { version = "1.0", optional = true }
|
||||||
|
|||||||
117
infra/src/auth/mod.rs
Normal file
117
infra/src/auth/mod.rs
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
//! Authentication infrastructure
|
||||||
|
//!
|
||||||
|
//! This module contains the concrete implementation of authentication mechanisms.
|
||||||
|
|
||||||
|
#[cfg(feature = "auth-axum-login")]
|
||||||
|
pub mod backend {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use axum_login::{AuthnBackend, UserId};
|
||||||
|
use password_auth::verify_password;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tower_sessions::SessionManagerLayer;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use domain::{User, UserRepository};
|
||||||
|
|
||||||
|
// We use the same session store as defined in infra
|
||||||
|
use crate::session_store::InfraSessionStore;
|
||||||
|
|
||||||
|
/// Wrapper around domain User to implement AuthUser
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct AuthUser(pub User);
|
||||||
|
|
||||||
|
impl axum_login::AuthUser for AuthUser {
|
||||||
|
type Id = Uuid;
|
||||||
|
|
||||||
|
fn id(&self) -> Self::Id {
|
||||||
|
self.0.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn session_auth_hash(&self) -> &[u8] {
|
||||||
|
// Use password hash to invalidate sessions if password changes
|
||||||
|
self.0
|
||||||
|
.password_hash
|
||||||
|
.as_ref()
|
||||||
|
.map(|s| s.as_bytes())
|
||||||
|
.unwrap_or(&[])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AuthBackend {
|
||||||
|
pub user_repo: Arc<dyn UserRepository>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthBackend {
|
||||||
|
pub fn new(user_repo: Arc<dyn UserRepository>) -> Self {
|
||||||
|
Self { user_repo }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize)]
|
||||||
|
pub struct Credentials {
|
||||||
|
pub email: String,
|
||||||
|
pub password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
pub enum AuthError {
|
||||||
|
#[error(transparent)]
|
||||||
|
Anyhow(#[from] anyhow::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthnBackend for AuthBackend {
|
||||||
|
type User = AuthUser;
|
||||||
|
type Credentials = Credentials;
|
||||||
|
type Error = AuthError;
|
||||||
|
|
||||||
|
async fn authenticate(
|
||||||
|
&self,
|
||||||
|
creds: Self::Credentials,
|
||||||
|
) -> Result<Option<Self::User>, Self::Error> {
|
||||||
|
let user = self
|
||||||
|
.user_repo
|
||||||
|
.find_by_email(&creds.email)
|
||||||
|
.await
|
||||||
|
.map_err(|e| AuthError::Anyhow(anyhow::anyhow!(e)))?;
|
||||||
|
|
||||||
|
if let Some(user) = user {
|
||||||
|
if let Some(hash) = &user.password_hash {
|
||||||
|
// Verify password
|
||||||
|
if verify_password(&creds.password, hash).is_ok() {
|
||||||
|
return Ok(Some(AuthUser(user)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_user(
|
||||||
|
&self,
|
||||||
|
user_id: &UserId<Self>,
|
||||||
|
) -> Result<Option<Self::User>, Self::Error> {
|
||||||
|
let user = self
|
||||||
|
.user_repo
|
||||||
|
.find_by_id(*user_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| AuthError::Anyhow(anyhow::anyhow!(e)))?;
|
||||||
|
|
||||||
|
Ok(user.map(AuthUser))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type AuthSession = axum_login::AuthSession<AuthBackend>;
|
||||||
|
pub type AuthManagerLayer = axum_login::AuthManagerLayer<AuthBackend, InfraSessionStore>;
|
||||||
|
|
||||||
|
pub async fn setup_auth_layer(
|
||||||
|
session_layer: SessionManagerLayer<InfraSessionStore>,
|
||||||
|
user_repo: Arc<dyn UserRepository>,
|
||||||
|
) -> Result<AuthManagerLayer, AuthError> {
|
||||||
|
let backend = AuthBackend::new(user_repo);
|
||||||
|
|
||||||
|
let auth_layer = axum_login::AuthManagerLayerBuilder::new(backend, session_layer).build();
|
||||||
|
Ok(auth_layer)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
//! - [`db::create_pool`] - Create a database connection pool
|
//! - [`db::create_pool`] - Create a database connection pool
|
||||||
//! - [`db::run_migrations`] - Run database migrations
|
//! - [`db::run_migrations`] - Run database migrations
|
||||||
|
|
||||||
|
pub mod auth;
|
||||||
pub mod db;
|
pub mod db;
|
||||||
pub mod factory;
|
pub mod factory;
|
||||||
pub mod session_store;
|
pub mod session_store;
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
pub use k_core::session::store::InfraSessionStore;
|
pub use k_core::session::store::InfraSessionStore;
|
||||||
|
pub use tower_sessions::{Expiry, SessionManagerLayer};
|
||||||
|
|||||||
Reference in New Issue
Block a user