reinhardt_admin/server/
login.rs1use crate::adapters::LoginResponse;
10use reinhardt_pages::server_fn::{ServerFnError, server_fn};
11
12#[cfg(server)]
13use super::admin_auth::AdminLoginAuthenticator;
14#[cfg(server)]
15use super::security::{build_admin_auth_cookie, require_csrf_header, require_csrf_token};
16#[cfg(server)]
17use crate::adapters::{AdminDatabase, AdminSite};
18#[cfg(server)]
19use crate::core::{AdminDatabaseKey, AdminSiteKey};
20#[cfg(server)]
21use reinhardt_auth::JwtAuth;
22#[cfg(server)]
23use reinhardt_di::Depends;
24#[cfg(server)]
25use reinhardt_pages::server_fn::ServerFnRequest;
26
27#[server_fn]
66pub async fn admin_login(
67 username: String,
68 password: String,
69 csrf_token: String,
70 #[inject] http_request: ServerFnRequest,
71 #[inject] db: Depends<AdminDatabaseKey, AdminDatabase>,
72 #[inject] site: Depends<AdminSiteKey, AdminSite>,
73 #[inject] authenticator: AdminLoginAuthenticator,
74) -> Result<LoginResponse, ServerFnError> {
75 require_csrf_token(&csrf_token, &http_request.inner().headers)?;
77
78 complete_admin_login(username, password, http_request, db, site, authenticator).await
79}
80
81#[server_fn]
83pub async fn admin_login_with_header(
84 username: String,
85 password: String,
86 #[inject] http_request: ServerFnRequest,
87 #[inject] db: Depends<AdminDatabaseKey, AdminDatabase>,
88 #[inject] site: Depends<AdminSiteKey, AdminSite>,
89 #[inject] authenticator: AdminLoginAuthenticator,
90) -> Result<LoginResponse, ServerFnError> {
91 require_csrf_header(&http_request.inner().headers)?;
92
93 complete_admin_login(username, password, http_request, db, site, authenticator).await
94}
95
96#[cfg(server)]
97async fn complete_admin_login(
98 username: String,
99 password: String,
100 http_request: ServerFnRequest,
101 db: Depends<AdminDatabaseKey, AdminDatabase>,
102 site: Depends<AdminSiteKey, AdminSite>,
103 authenticator: AdminLoginAuthenticator,
104) -> Result<LoginResponse, ServerFnError> {
105 let jwt_secret = site.jwt_secret().ok_or_else(|| {
107 ::tracing::error!("admin_login: JWT secret not configured on AdminSite");
108 ServerFnError::server(500, "Admin login is not configured")
109 })?;
110 let jwt_auth = JwtAuth::new(jwt_secret);
111
112 let user_info = (authenticator.0)(username.clone(), password, db.connection_arc())
114 .await
115 .map_err(|e| {
116 ::tracing::warn!(error = ?e, "admin_login: Authentication failed");
117 ServerFnError::server(500, "Internal authentication error")
118 })?;
119
120 let user_info = user_info.ok_or_else(|| {
121 ServerFnError::server(401, "Invalid username or password")
123 })?;
124
125 let token = jwt_auth
127 .generate_token(
128 user_info.user_id.clone(),
129 user_info.username.clone(),
130 user_info.is_staff,
131 user_info.is_superuser,
132 )
133 .map_err(|e| {
134 ::tracing::error!(error = ?e, "admin_login: JWT token generation failed");
135 ServerFnError::server(500, "Token generation failed")
136 })?;
137
138 let is_secure = http_request.inner().is_secure;
142 let cookie = build_admin_auth_cookie(&token, is_secure);
143 http_request.add_response_cookie(cookie);
144
145 Ok(LoginResponse {
148 token: String::new(),
149 username: user_info.username,
150 user_id: user_info.user_id,
151 is_staff: user_info.is_staff,
152 is_superuser: user_info.is_superuser,
153 })
154}
155
156#[cfg(all(test, server))]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_login_response_serialization() {
162 let response = LoginResponse {
164 token: "eyJhbGciOiJIUzI1NiJ9.test".to_string(),
165 username: "admin".to_string(),
166 user_id: "550e8400-e29b-41d4-a716-446655440000".to_string(),
167 is_staff: true,
168 is_superuser: false,
169 };
170
171 let json = serde_json::to_string(&response).expect("serialization should succeed");
173 let deserialized: LoginResponse =
174 serde_json::from_str(&json).expect("deserialization should succeed");
175
176 assert_eq!(deserialized.token, response.token);
178 assert_eq!(deserialized.username, response.username);
179 assert_eq!(deserialized.user_id, response.user_id);
180 assert!(deserialized.is_staff);
181 assert!(!deserialized.is_superuser);
182 }
183}