1use crate::api::{ApiResponse, ApiState, extract_bearer_token};
6use axum::{Json, extract::State, http::HeaderMap};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Deserialize)]
11pub struct LoginRequest {
12 pub username: String,
13 pub password: String,
14 #[serde(default)]
15 pub mfa_code: Option<String>,
16 #[serde(default)]
17 pub remember_me: bool,
18}
19
20#[derive(Debug, Serialize)]
22pub struct LoginResponse {
23 pub access_token: String,
24 pub refresh_token: String,
25 pub token_type: String,
26 pub expires_in: u64,
27 pub user: UserInfo,
28}
29
30#[derive(Debug, Serialize)]
32pub struct UserInfo {
33 pub id: String,
34 pub username: String,
35 pub roles: Vec<String>,
36 pub permissions: Vec<String>,
37}
38
39#[derive(Debug, Deserialize)]
41pub struct RefreshRequest {
42 pub refresh_token: String,
43}
44
45#[derive(Debug, Serialize)]
47pub struct RefreshResponse {
48 pub access_token: String,
49 pub token_type: String,
50 pub expires_in: u64,
51}
52
53#[derive(Debug, Deserialize)]
55pub struct LogoutRequest {
56 #[serde(default)]
57 pub refresh_token: Option<String>,
58}
59
60pub async fn login(
62 State(state): State<ApiState>,
63 Json(req): Json<LoginRequest>,
64) -> ApiResponse<LoginResponse> {
65 if req.username.is_empty() || req.password.is_empty() {
67 return ApiResponse::validation_error_typed("Username and password are required");
68 }
69
70 let credential = crate::authentication::credentials::Credential::Password {
72 username: req.username.clone(),
73 password: req.password.clone(),
74 };
75
76 match state
78 .auth_framework
79 .authenticate("password", credential)
80 .await
81 {
82 Ok(auth_result) => match auth_result {
83 crate::auth::AuthResult::Success(token) => {
84 let user_info = UserInfo {
86 id: token.user_id.clone(),
87 username: req.username,
88 roles: token.roles.clone(),
89 permissions: token.permissions.clone(),
90 };
91
92 let token_lifetime = std::time::Duration::from_secs(3600); let access_token = match state.auth_framework.token_manager().create_jwt_token(
95 &token.user_id,
96 token.permissions.clone(),
97 Some(token_lifetime),
98 ) {
99 Ok(jwt) => jwt,
100 Err(e) => {
101 tracing::error!("Failed to create JWT token: {}", e);
102 return ApiResponse::error_typed(
103 "TOKEN_CREATION_FAILED",
104 "Failed to create access token",
105 );
106 }
107 };
108
109 let refresh_token_lifetime = std::time::Duration::from_secs(86400 * 7); let refresh_token = match state.auth_framework.token_manager().create_jwt_token(
112 &token.user_id,
113 vec!["refresh".to_string()],
114 Some(refresh_token_lifetime),
115 ) {
116 Ok(jwt) => jwt,
117 Err(e) => {
118 tracing::error!("Failed to create refresh token: {}", e);
119 return ApiResponse::error_typed(
120 "TOKEN_CREATION_FAILED",
121 "Failed to create refresh token",
122 );
123 }
124 };
125
126 let response = LoginResponse {
127 access_token,
128 refresh_token,
129 token_type: "Bearer".to_string(),
130 expires_in: 3600, user: user_info,
132 };
133
134 ApiResponse::success(response)
135 }
136 crate::auth::AuthResult::MfaRequired(_challenge) => {
137 ApiResponse::error_typed("MFA_REQUIRED", "Multi-factor authentication required")
139 }
140 crate::auth::AuthResult::Failure(reason) => {
141 tracing::warn!(
142 "Authentication failed for user '{}': {}",
143 req.username,
144 reason
145 );
146 ApiResponse::unauthorized_typed(
147 "INVALID_CREDENTIALS",
148 "Invalid username or password",
149 )
150 }
151 },
152 Err(e) => {
153 tracing::warn!("Authentication failed for user '{}': {}", req.username, e);
155 if matches!(e, crate::errors::AuthError::AuthMethod { .. }) {
156 ApiResponse::unauthorized_typed(
157 "INVALID_CREDENTIALS",
158 "Invalid username or password",
159 )
160 } else {
161 ApiResponse::unauthorized_typed("AUTH_ERROR", "Authentication failed")
162 }
163 }
164 }
165}
166
167pub async fn refresh_token(
169 State(state): State<ApiState>,
170 Json(req): Json<RefreshRequest>,
171) -> ApiResponse<RefreshResponse> {
172 if req.refresh_token.is_empty() {
173 return ApiResponse::validation_error_typed("Refresh token is required");
174 }
175
176 match state
178 .auth_framework
179 .token_manager()
180 .validate_jwt_token(&req.refresh_token)
181 {
182 Ok(claims) => {
183 if !claims.scope.contains("refresh") {
185 return ApiResponse::unauthorized_typed(
186 "INVALID_TOKEN",
187 "Token is not a refresh token",
188 );
189 }
190
191 let token_lifetime = std::time::Duration::from_secs(3600); let new_access_token = match state.auth_framework.token_manager().create_jwt_token(
194 &claims.sub,
195 vec!["read".to_string(), "write".to_string()], Some(token_lifetime),
197 ) {
198 Ok(jwt) => jwt,
199 Err(e) => {
200 tracing::error!("Failed to create new access token: {}", e);
201 return ApiResponse::error_typed(
202 "TOKEN_CREATION_FAILED",
203 "Failed to create new access token",
204 );
205 }
206 };
207
208 let response = RefreshResponse {
209 access_token: new_access_token,
210 token_type: "Bearer".to_string(),
211 expires_in: 3600,
212 };
213
214 ApiResponse::success(response)
215 }
216 Err(e) => {
217 tracing::warn!("Invalid refresh token: {}", e);
218 ApiResponse::unauthorized_typed("INVALID_TOKEN", "Invalid or expired refresh token")
219 }
220 }
221}
222
223pub async fn logout(
225 State(_state): State<ApiState>,
226 headers: HeaderMap,
227 Json(req): Json<LogoutRequest>,
228) -> ApiResponse<()> {
229 if let Some(token) = extract_bearer_token(&headers) {
231 tracing::info!("Logging out user with token: {}", &token[..10]);
233 }
234
235 if let Some(ref refresh_token) = req.refresh_token {
237 tracing::info!("Invalidating refresh token: {}", &refresh_token[..10]);
238 }
239
240 ApiResponse::<()>::ok_with_message("Successfully logged out")
241}
242
243pub async fn validate_token(
246 State(state): State<ApiState>,
247 headers: HeaderMap,
248) -> ApiResponse<UserInfo> {
249 match extract_bearer_token(&headers) {
250 Some(token) => {
251 match crate::api::validate_api_token(&state.auth_framework, &token).await {
252 Ok(auth_token) => {
253 let username = match state
255 .auth_framework
256 .get_user_profile(&auth_token.user_id)
257 .await
258 {
259 Ok(profile) => profile
260 .username
261 .unwrap_or_else(|| format!("user_{}", auth_token.user_id)),
262 Err(_) => format!("user_{}", auth_token.user_id), };
264
265 let user_info = UserInfo {
266 id: auth_token.user_id,
267 username,
268 roles: auth_token.roles,
269 permissions: auth_token.permissions,
270 };
271 ApiResponse::success(user_info)
272 }
273 Err(_e) => ApiResponse::error_typed("AUTH_ERROR", "Token validation failed"),
274 }
275 }
276 None => ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required"),
277 }
278}
279
280pub async fn list_providers(State(_state): State<ApiState>) -> ApiResponse<Vec<ProviderInfo>> {
283 let providers = vec![
284 ProviderInfo {
285 name: "google".to_string(),
286 display_name: "Google".to_string(),
287 auth_url: "/oauth/google".to_string(),
288 },
289 ProviderInfo {
290 name: "github".to_string(),
291 display_name: "GitHub".to_string(),
292 auth_url: "/oauth/github".to_string(),
293 },
294 ProviderInfo {
295 name: "microsoft".to_string(),
296 display_name: "Microsoft".to_string(),
297 auth_url: "/oauth/microsoft".to_string(),
298 },
299 ];
300
301 ApiResponse::success(providers)
302}
303
304#[derive(Debug, Serialize)]
306pub struct ProviderInfo {
307 pub name: String,
308 pub display_name: String,
309 pub auth_url: String,
310}
311
312#[derive(Debug, Deserialize)]
314pub struct RegisterRequest {
315 pub username: String,
316 pub email: String,
317 pub password: String,
318 #[serde(default)]
319 pub full_name: Option<String>,
320}
321
322#[derive(Debug, Serialize)]
324pub struct RegisterResponse {
325 pub user_id: String,
326 pub username: String,
327 pub email: String,
328 pub created_at: String,
329}
330
331pub async fn register(
334 State(state): State<ApiState>,
335 Json(req): Json<RegisterRequest>,
336) -> ApiResponse<RegisterResponse> {
337 if req.username.is_empty() || req.password.is_empty() || req.email.is_empty() {
339 return ApiResponse::validation_error_typed("Username, password, and email are required");
340 }
341
342 match state
344 .auth_framework
345 .register_user(&req.username, &req.email, &req.password)
346 .await
347 {
348 Ok(user_id) => {
349 let created_at = chrono::Utc::now().to_rfc3339();
350
351 tracing::info!(
352 "New user registered via API: {} ({})",
353 req.username,
354 user_id
355 );
356
357 let response = RegisterResponse {
358 user_id,
359 username: req.username,
360 email: req.email,
361 created_at,
362 };
363
364 ApiResponse::success(response)
365 }
366 Err(e) => {
367 tracing::warn!("User registration failed: {:?}", e);
368
369 let error_msg = e.to_string();
371 if error_msg.contains("Username already exists") {
372 ApiResponse::conflict_typed("USERNAME_EXISTS", "Username already exists")
373 } else if error_msg.contains("Email address already registered") {
374 ApiResponse::conflict_typed("EMAIL_EXISTS", "Email address already registered")
375 } else if error_msg.contains("validation") || error_msg.contains("Password") {
376 ApiResponse::validation_error_typed(&error_msg)
377 } else {
378 ApiResponse::error_typed("REGISTRATION_FAILED", "Failed to create user account")
379 }
380 }
381 }
382}
383
384#[derive(Debug, Deserialize)]
386pub struct AuthenticateRequest {
387 pub method: String,
388 pub credential: serde_json::Value,
389}
390
391pub async fn authenticate(
393 State(state): State<ApiState>,
394 Json(req): Json<AuthenticateRequest>,
395) -> ApiResponse<LoginResponse> {
396 let credential = match req.method.as_str() {
398 "password" => {
399 let username = req.credential["username"]
400 .as_str()
401 .unwrap_or_default()
402 .to_string();
403 let password = req.credential["password"]
404 .as_str()
405 .unwrap_or_default()
406 .to_string();
407
408 if username.is_empty() || password.is_empty() {
409 return ApiResponse::validation_error_typed("Username and password are required");
410 }
411
412 crate::authentication::credentials::Credential::Password { username, password }
413 }
414 "jwt" => {
415 let token = req.credential["token"]
416 .as_str()
417 .unwrap_or_default()
418 .to_string();
419
420 if token.is_empty() {
421 return ApiResponse::validation_error_typed("JWT token is required");
422 }
423
424 crate::authentication::credentials::Credential::Jwt { token }
425 }
426 "api_key" => {
427 let key = req.credential["key"]
428 .as_str()
429 .unwrap_or_default()
430 .to_string();
431
432 if key.is_empty() {
433 return ApiResponse::validation_error_typed("API key is required");
434 }
435
436 crate::authentication::credentials::Credential::ApiKey { key }
437 }
438 "bearer" => {
439 let token = req.credential["token"]
440 .as_str()
441 .unwrap_or_default()
442 .to_string();
443
444 if token.is_empty() {
445 return ApiResponse::validation_error_typed("Bearer token is required");
446 }
447
448 crate::authentication::credentials::Credential::Bearer { token }
449 }
450 _ => {
451 return ApiResponse::validation_error_typed(format!(
452 "Unsupported authentication method: {}",
453 req.method
454 ));
455 }
456 };
457
458 match state
460 .auth_framework
461 .authenticate(&req.method, credential)
462 .await
463 {
464 Ok(auth_result) => match auth_result {
465 crate::auth::AuthResult::Success(token) => {
466 let username = match req.method.as_str() {
468 "password" => req.credential["username"]
469 .as_str()
470 .unwrap_or("unknown")
471 .to_string(),
472 _ => token.user_id.clone(),
473 };
474
475 let user_info = UserInfo {
477 id: token.user_id.clone(),
478 username,
479 roles: token.roles.clone(),
480 permissions: token.permissions.clone(),
481 };
482
483 let (access_token, refresh_token) = if req.method == "password" {
485 let token_lifetime = std::time::Duration::from_secs(3600); let access = match state.auth_framework.token_manager().create_jwt_token(
488 &token.user_id,
489 token.permissions.clone(),
490 Some(token_lifetime),
491 ) {
492 Ok(jwt) => jwt,
493 Err(e) => {
494 tracing::error!("Failed to create JWT token: {}", e);
495 return ApiResponse::error_typed(
496 "TOKEN_CREATION_FAILED",
497 "Failed to create access token",
498 );
499 }
500 };
501
502 let refresh_token_lifetime = std::time::Duration::from_secs(86400 * 7); let refresh = match state.auth_framework.token_manager().create_jwt_token(
504 &token.user_id,
505 vec!["refresh".to_string()],
506 Some(refresh_token_lifetime),
507 ) {
508 Ok(jwt) => jwt,
509 Err(e) => {
510 tracing::error!("Failed to create refresh token: {}", e);
511 return ApiResponse::error_typed(
512 "TOKEN_CREATION_FAILED",
513 "Failed to create refresh token",
514 );
515 }
516 };
517
518 (access, refresh)
519 } else {
520 (token.access_token.clone(), String::new())
522 };
523
524 let response = LoginResponse {
525 access_token,
526 refresh_token,
527 token_type: "Bearer".to_string(),
528 expires_in: 3600,
529 user: user_info,
530 };
531
532 ApiResponse::success(response)
533 }
534 crate::auth::AuthResult::MfaRequired(_challenge) => {
535 ApiResponse::error_typed("MFA_REQUIRED", "Multi-factor authentication required")
536 }
537 crate::auth::AuthResult::Failure(reason) => {
538 tracing::warn!("Authentication failed: {}", reason);
539 ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required")
540 }
541 },
542 Err(e) => {
543 tracing::error!("Authentication error: {:?}", e);
544 ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required")
545 }
546 }
547}
548
549#[derive(Debug, Deserialize)]
551pub struct CreateApiKeyRequest {
552 pub name: String,
553 #[serde(default)]
554 pub scopes: Vec<String>,
555 #[serde(default)]
556 pub permissions: Vec<String>,
557 #[serde(default)]
558 pub expires_in_days: Option<u64>,
559}
560
561#[derive(Debug, Serialize)]
563pub struct ApiKeyResponse {
564 pub key: String,
565 pub name: String,
566 pub user_id: String,
567 pub scopes: Vec<String>,
568 pub permissions: Vec<String>,
569 pub created_at: String,
570 pub expires_at: Option<String>,
571}
572
573#[derive(Debug, Serialize)]
575pub struct ApiKeyInfo {
576 pub name: String,
577 pub key_prefix: String, pub scopes: Vec<String>,
579 pub created_at: String,
580 pub expires_at: Option<String>,
581 pub last_used: Option<String>,
582 pub use_count: u64,
583}
584
585pub async fn create_api_key(
587 State(state): State<ApiState>,
588 headers: HeaderMap,
589 Json(req): Json<CreateApiKeyRequest>,
590) -> ApiResponse<ApiKeyResponse> {
591 let token = match extract_bearer_token(&headers) {
593 Some(t) => t,
594 None => return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required"),
595 };
596
597 let claims = match state
599 .auth_framework
600 .token_manager()
601 .validate_jwt_token(&token)
602 {
603 Ok(c) => c,
604 Err(_) => {
605 return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required");
606 }
607 };
608
609 let user_id = claims.sub.clone();
610
611 if req.name.is_empty() {
613 return ApiResponse::validation_error_typed("API key name is required");
614 }
615
616 let api_key = format!("ak_{}", uuid::Uuid::new_v4().to_string().replace("-", ""));
618
619 let created_at = chrono::Utc::now();
621 let expires_at = req
622 .expires_in_days
623 .map(|days| created_at + chrono::Duration::days(days as i64));
624
625 let scopes = if req.scopes.is_empty() {
627 vec!["api_access".to_string()]
628 } else {
629 req.scopes
630 };
631
632 let permissions = if req.permissions.is_empty() {
633 vec!["read".to_string()]
634 } else {
635 req.permissions
636 };
637
638 let key_data = serde_json::json!({
640 "user_id": user_id,
641 "name": req.name,
642 "scopes": scopes,
643 "permissions": permissions,
644 "created_at": created_at.to_rfc3339(),
645 "expires_at": expires_at.map(|dt| dt.to_rfc3339()),
646 "last_used": Option::<String>::None,
647 "use_count": 0,
648 });
649
650 let key_data_str = serde_json::to_string(&key_data).unwrap();
651 let storage_key = format!("api_key:{}", api_key);
652
653 match state
654 .auth_framework
655 .storage()
656 .store_kv(&storage_key, key_data_str.as_bytes(), None)
657 .await
658 {
659 Ok(_) => {
660 let index_key = format!("user_api_keys:{}", user_id);
662 let mut key_ids = match state.auth_framework.storage().get_kv(&index_key).await {
663 Ok(Some(data)) => serde_json::from_slice::<Vec<String>>(&data).unwrap_or_default(),
664 _ => Vec::new(),
665 };
666
667 key_ids.push(api_key.clone());
668
669 if let Ok(index_data) = serde_json::to_vec(&key_ids) {
670 let _ = state
671 .auth_framework
672 .storage()
673 .store_kv(&index_key, &index_data, None)
674 .await;
675 }
676
677 tracing::info!("API key created for user {}: {}", user_id, req.name);
678
679 let response = ApiKeyResponse {
680 key: api_key,
681 name: req.name,
682 user_id,
683 scopes,
684 permissions,
685 created_at: created_at.to_rfc3339(),
686 expires_at: expires_at.map(|dt| dt.to_rfc3339()),
687 };
688
689 ApiResponse::success(response)
690 }
691 Err(e) => {
692 tracing::error!("Failed to store API key: {:?}", e);
693 ApiResponse::error_typed("API_KEY_CREATION_FAILED", "Failed to create API key")
694 }
695 }
696}
697
698pub async fn list_api_keys(
700 State(state): State<ApiState>,
701 headers: HeaderMap,
702) -> ApiResponse<Vec<ApiKeyInfo>> {
703 let token = match extract_bearer_token(&headers) {
705 Some(t) => t,
706 None => return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required"),
707 };
708
709 let claims = match state
711 .auth_framework
712 .token_manager()
713 .validate_jwt_token(&token)
714 {
715 Ok(c) => c,
716 Err(_) => {
717 return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required");
718 }
719 };
720
721 let user_id = claims.sub.clone();
722
723 let index_key = format!("user_api_keys:{}", user_id);
725 let key_ids = match state.auth_framework.storage().get_kv(&index_key).await {
726 Ok(Some(data)) => match serde_json::from_slice::<Vec<String>>(&data) {
727 Ok(ids) => ids,
728 Err(e) => {
729 tracing::error!("Failed to deserialize key index: {}", e);
730 return ApiResponse::error_typed("INTERNAL_ERROR", "Failed to read API keys");
731 }
732 },
733 Ok(None) => Vec::new(),
734 Err(e) => {
735 tracing::error!("Failed to read key index: {}", e);
736 return ApiResponse::error_typed("INTERNAL_ERROR", "Failed to read API keys");
737 }
738 };
739
740 let mut keys = Vec::new();
742 for key_id in key_ids {
743 let storage_key = format!("api_key:{}", key_id);
744 if let Ok(Some(data)) = state.auth_framework.storage().get_kv(&storage_key).await
745 && let Ok(key_data) = serde_json::from_slice::<serde_json::Value>(&data)
746 {
747 let key_prefix = if key_id.len() > 12 {
749 format!("{}...", &key_id[..12])
750 } else {
751 key_id.clone()
752 };
753
754 keys.push(ApiKeyInfo {
755 name: key_data["name"].as_str().unwrap_or("Unknown").to_string(),
756 key_prefix,
757 scopes: key_data["scopes"]
758 .as_array()
759 .and_then(|arr| {
760 arr.iter()
761 .filter_map(|v| v.as_str().map(|s| s.to_string()))
762 .collect::<Vec<_>>()
763 .into()
764 })
765 .unwrap_or_default(),
766 created_at: key_data["created_at"].as_str().unwrap_or("").to_string(),
767 expires_at: key_data["expires_at"].as_str().map(|s| s.to_string()),
768 last_used: key_data["last_used"].as_str().map(|s| s.to_string()),
769 use_count: key_data["use_count"].as_u64().unwrap_or(0),
770 });
771 }
772 }
773
774 tracing::info!("Listed {} API keys for user {}", keys.len(), user_id);
775 ApiResponse::success(keys)
776}
777
778#[derive(Debug, Deserialize)]
780pub struct RevokeApiKeyRequest {
781 pub key_id: String,
782}
783
784pub async fn revoke_api_key(
785 State(state): State<ApiState>,
786 headers: HeaderMap,
787 Json(req): Json<RevokeApiKeyRequest>,
788) -> ApiResponse<serde_json::Value> {
789 let token = match extract_bearer_token(&headers) {
791 Some(t) => t,
792 None => return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required"),
793 };
794
795 let claims = match state
797 .auth_framework
798 .token_manager()
799 .validate_jwt_token(&token)
800 {
801 Ok(c) => c,
802 Err(_) => {
803 return ApiResponse::unauthorized_typed("UNAUTHORIZED", "Authentication required");
804 }
805 };
806
807 let user_id = claims.sub.clone();
808
809 let storage_key = format!("api_key:{}", req.key_id);
811
812 match state.auth_framework.storage().get_kv(&storage_key).await {
814 Ok(Some(data)) => {
815 let key_data_str = String::from_utf8_lossy(&data);
816 let key_data: serde_json::Value = match serde_json::from_str(&key_data_str) {
817 Ok(d) => d,
818 Err(_) => return ApiResponse::error_typed("INVALID_KEY_DATA", "Invalid key data"),
819 };
820
821 let key_user_id = key_data["user_id"].as_str().unwrap_or("");
822 if key_user_id != user_id {
823 return ApiResponse::error_typed("FORBIDDEN", "API key does not belong to user");
824 }
825
826 match state.auth_framework.storage().delete_kv(&storage_key).await {
828 Ok(_) => {
829 let index_key = format!("user_api_keys:{}", user_id);
831 if let Ok(Some(data)) = state.auth_framework.storage().get_kv(&index_key).await
832 && let Ok(mut key_ids) = serde_json::from_slice::<Vec<String>>(&data)
833 {
834 key_ids.retain(|id| id != &req.key_id);
835 if let Ok(index_data) = serde_json::to_vec(&key_ids) {
836 let _ = state
837 .auth_framework
838 .storage()
839 .store_kv(&index_key, &index_data, None)
840 .await;
841 }
842 }
843
844 tracing::info!("API key revoked: {} by user {}", req.key_id, user_id);
845 ApiResponse::success(serde_json::json!({
846 "message": "API key revoked successfully"
847 }))
848 }
849 Err(e) => {
850 tracing::error!("Failed to delete API key: {:?}", e);
851 ApiResponse::error_typed("REVOCATION_FAILED", "Failed to revoke API key")
852 }
853 }
854 }
855 Ok(None) => ApiResponse::error_typed("NOT_FOUND", "API key not found"),
856 Err(e) => {
857 tracing::error!("Failed to retrieve API key: {:?}", e);
858 ApiResponse::error_typed("RETRIEVAL_FAILED", "Failed to retrieve API key")
859 }
860 }
861}