lmrc-cli 0.3.16

CLI tool for scaffolding LMRC Stack infrastructure projects
Documentation
//! Authentication HTTP handlers

use axum::{
    extract::State,
    response::IntoResponse,
    Json,
};
use cookie::{Cookie, SameSite};
use serde::{Deserialize, Serialize};
use validator::Validate;

use crate::error::Result;
use crate::state::AppState;
use crate::auth::ports::AuthUser;

#[derive(Debug, Deserialize, Validate)]
pub struct LoginRequest {
    #[validate(email)]
    pub email: String,
    #[validate(length(min = 8))]
    pub password: String,
}

#[derive(Debug, Serialize)]
pub struct LoginResponse {
    pub user: UserInfo,
}

#[derive(Debug, Serialize)]
pub struct UserInfo {
    pub id: i64,
    pub email: String,
    pub role: String,
}

impl From<AuthUser> for UserInfo {
    fn from(user: AuthUser) -> Self {
        Self {
            id: user.id,
            email: user.email,
            role: user.role,
        }
    }
}

/// Infrastructure login handler
pub async fn infra_login(
    State(state): State<AppState>,
    Json(req): Json<LoginRequest>,
) -> Result<impl IntoResponse> {
    req.validate()?;

    // Authenticate
    let user = state.infra_auth.authenticate(&req.email, &req.password).await?;

    // Create session
    let session = state.infra_auth.create_session(user.id).await?;

    // Create session cookie
    let cookie = Cookie::build(("infra_session", session.token))
        .path("/")
        .http_only(true)
        .same_site(SameSite::Strict)
        .max_age(cookie::time::Duration::hours(state.config.auth.session_expiration_hours))
        .build();

    let response = LoginResponse {
        user: user.into(),
    };

    use axum::response::AppendHeaders;

    Ok((
        AppendHeaders([(axum::http::header::SET_COOKIE, cookie.to_string())]),
        Json(response),
    ))
}

/// Infrastructure logout handler
pub async fn infra_logout(
    State(state): State<AppState>,
    axum::extract::Extension(session_token): axum::extract::Extension<String>,
) -> Result<impl IntoResponse> {
    // Destroy session
    state.infra_auth.destroy_session(&session_token).await?;

    // Clear cookie
    let cookie = Cookie::build(("infra_session", ""))
        .path("/")
        .max_age(cookie::time::Duration::ZERO)
        .build();

    use axum::response::AppendHeaders;

    Ok((
        AppendHeaders([(axum::http::header::SET_COOKIE, cookie.to_string())]),
        Json(serde_json::json!({"message": "Logged out successfully"})),
    ))
}

/// Get current infrastructure user
pub async fn infra_me(
    axum::extract::Extension(user): axum::extract::Extension<AuthUser>,
) -> Result<impl IntoResponse> {
    Ok(Json(UserInfo::from(user)))
}