use crate::adapters::LoginResponse;
use reinhardt_pages::server_fn::{ServerFnError, server_fn};
#[cfg(server)]
use super::admin_auth::AdminLoginAuthenticator;
#[cfg(server)]
use super::security::{build_admin_auth_cookie, require_csrf_token};
#[cfg(server)]
use crate::adapters::AdminSite;
#[cfg(server)]
use reinhardt_auth::JwtAuth;
#[cfg(server)]
use reinhardt_db::orm::DatabaseConnection;
#[cfg(server)]
use reinhardt_di::Depends;
#[cfg(server)]
use reinhardt_pages::server_fn::ServerFnRequest;
#[server_fn]
pub async fn admin_login(
username: String,
password: String,
csrf_token: String,
#[inject] http_request: ServerFnRequest,
#[inject] db: Depends<DatabaseConnection>,
#[inject] site: Depends<AdminSite>,
#[inject] authenticator: Depends<AdminLoginAuthenticator>,
) -> Result<LoginResponse, ServerFnError> {
require_csrf_token(&csrf_token, &http_request.inner().headers)?;
let jwt_secret = site.jwt_secret().ok_or_else(|| {
::tracing::error!("admin_login: JWT secret not configured on AdminSite");
ServerFnError::server(500, "Admin login is not configured")
})?;
let jwt_auth = JwtAuth::new(jwt_secret);
let user_info = (authenticator.0)(username.clone(), password, db.as_arc().clone())
.await
.map_err(|e| {
::tracing::warn!(error = ?e, "admin_login: Authentication failed");
ServerFnError::server(500, "Internal authentication error")
})?;
let user_info = user_info.ok_or_else(|| {
ServerFnError::server(401, "Invalid username or password")
})?;
let token = jwt_auth
.generate_token(
user_info.user_id.clone(),
user_info.username.clone(),
user_info.is_staff,
user_info.is_superuser,
)
.map_err(|e| {
::tracing::error!(error = ?e, "admin_login: JWT token generation failed");
ServerFnError::server(500, "Token generation failed")
})?;
let is_secure = http_request.inner().is_secure;
let cookie = build_admin_auth_cookie(&token, is_secure);
http_request.add_response_cookie(cookie);
Ok(LoginResponse {
token: String::new(),
username: user_info.username,
user_id: user_info.user_id,
is_staff: user_info.is_staff,
is_superuser: user_info.is_superuser,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_login_response_serialization() {
let response = LoginResponse {
token: "eyJhbGciOiJIUzI1NiJ9.test".to_string(),
username: "admin".to_string(),
user_id: "550e8400-e29b-41d4-a716-446655440000".to_string(),
is_staff: true,
is_superuser: false,
};
let json = serde_json::to_string(&response).expect("serialization should succeed");
let deserialized: LoginResponse =
serde_json::from_str(&json).expect("deserialization should succeed");
assert_eq!(deserialized.token, response.token);
assert_eq!(deserialized.username, response.username);
assert_eq!(deserialized.user_id, response.user_id);
assert!(deserialized.is_staff);
assert!(!deserialized.is_superuser);
}
}