backend/src/api/v1/auth/mod.rs
Radical a602c2624f
All checks were successful
ci/woodpecker/push/build-and-publish Pipeline was successful
style: cargo fmt & clippy fixes
2025-07-20 16:30:46 +02:00

98 lines
2.7 KiB
Rust

use std::{
sync::Arc,
time::{SystemTime, UNIX_EPOCH},
};
use axum::{
Router,
extract::{Request, State},
middleware::{Next, from_fn_with_state},
response::IntoResponse,
routing::{delete, get, post},
};
use axum_extra::{
TypedHeader,
headers::{Authorization, authorization::Bearer},
};
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use serde::Serialize;
use uuid::Uuid;
use crate::{AppState, Conn, error::Error, schema::access_tokens::dsl};
mod devices;
mod login;
mod logout;
mod refresh;
mod register;
mod reset_password;
mod revoke;
mod verify_email;
#[derive(Serialize)]
pub struct Response {
access_token: String,
device_name: String,
}
pub fn router(app_state: Arc<AppState>) -> Router<Arc<AppState>> {
let router_with_auth = Router::new()
.route("/verify-email", get(verify_email::get))
.route("/verify-email", post(verify_email::post))
.route("/revoke", post(revoke::post))
.route("/devices", get(devices::get))
.layer(from_fn_with_state(app_state, CurrentUser::check_auth_layer));
Router::new()
.route("/register", post(register::post))
.route("/login", post(login::response))
.route("/logout", delete(logout::res))
.route("/refresh", post(refresh::post))
.route("/reset-password", get(reset_password::get))
.route("/reset-password", post(reset_password::post))
.merge(router_with_auth)
}
pub async fn check_access_token(access_token: &str, conn: &mut Conn) -> Result<Uuid, Error> {
let (uuid, created_at): (Uuid, i64) = dsl::access_tokens
.filter(dsl::token.eq(access_token))
.select((dsl::uuid, dsl::created_at))
.get_result(conn)
.await
.map_err(|error| {
if error == diesel::result::Error::NotFound {
Error::Unauthorized("Invalid access token".to_string())
} else {
Error::from(error)
}
})?;
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as i64;
let lifetime = current_time - created_at;
if lifetime > 3600 {
return Err(Error::Unauthorized("Invalid access token".to_string()));
}
Ok(uuid)
}
#[derive(Clone)]
pub struct CurrentUser<Uuid>(pub Uuid);
impl CurrentUser<Uuid> {
pub async fn check_auth_layer(
State(app_state): State<Arc<AppState>>,
TypedHeader(auth): TypedHeader<Authorization<Bearer>>,
mut req: Request,
next: Next,
) -> Result<impl IntoResponse, Error> {
let current_user =
CurrentUser(check_access_token(auth.token(), &mut app_state.pool.get().await?).await?);
req.extensions_mut().insert(current_user);
Ok(next.run(req).await)
}
}