1use crate::api_keys::ApiKeyManager;
4use crate::audit::AuditManager;
5use crate::config::Config;
6use crate::crypto::CryptoManager;
7use crate::error::{AuthError, Result};
8use crate::jwt::{JwtConfig, JwtManager};
9use crate::mfa::MfaManager;
10use crate::models::*;
11use crate::oauth2::OAuth2Manager;
12use crate::password::{PasswordManager, PasswordPolicy};
13use crate::permissions::PermissionManager;
14use crate::risk::{RiskConfig, RiskEngine};
15use crate::session::{SessionConfig, SessionManager};
16use chrono::{Duration, Utc};
17use std::collections::HashMap;
18use std::sync::Arc;
19use tokio::sync::RwLock;
20use uuid::Uuid;
21
22pub struct AuthClient {
23 config: Config,
24 jwt: Arc<JwtManager>,
25 password: Arc<PasswordManager>,
26 oauth2: Arc<OAuth2Manager>,
27 mfa: Arc<MfaManager>,
28 permissions: Arc<PermissionManager>,
29 sessions: Arc<SessionManager>,
30 api_keys: Arc<ApiKeyManager>,
31 risk: Arc<RiskEngine>,
32 audit: Arc<AuditManager>,
33 crypto: Arc<CryptoManager>,
34 users: Arc<RwLock<HashMap<Uuid, User>>>,
35}
36
37impl AuthClient {
38 pub async fn new(config: Config) -> Result<Self> {
39 config.validate()?;
40
41 let jwt_config = JwtConfig {
42 algorithm: parse_algorithm(&config.jwt.algorithm)
43 .map_err(|e| AuthError::ConfigError(e))?,
44 issuer: config.jwt.issuer.clone(),
45 audience: config.jwt.audience.clone(),
46 access_token_ttl: chrono::Duration::from_std(config.jwt.access_token_ttl)
47 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
48 refresh_token_ttl: chrono::Duration::from_std(config.jwt.refresh_token_ttl)
49 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
50 };
51
52 let jwt = Arc::new(JwtManager::new(
53 jwt_config,
54 &config.jwt.private_key,
55 &config.jwt.public_key,
56 )?);
57
58 let password_policy = PasswordPolicy {
59 min_length: config.password.min_length,
60 require_uppercase: config.password.require_uppercase,
61 require_lowercase: config.password.require_lowercase,
62 require_numbers: config.password.require_numbers,
63 require_special: config.password.require_special,
64 password_history: config.password.password_history,
65 };
66
67 let password = Arc::new(PasswordManager::new(
68 password_policy,
69 config.password.argon2_memory_cost,
70 config.password.argon2_time_cost,
71 config.password.argon2_parallelism,
72 )?);
73
74 let oauth2 = Arc::new(OAuth2Manager::new());
75 for provider in &config.oauth2_providers {
76 oauth2.register_provider(provider.clone()).await?;
77 }
78
79 let mfa = Arc::new(MfaManager::new(
80 config.mfa.totp_issuer.clone(),
81 config.mfa.totp_period,
82 config.mfa.totp_digits,
83 ));
84
85 let permissions = Arc::new(PermissionManager::new());
86
87 let session_config = SessionConfig {
88 idle_timeout: chrono::Duration::from_std(config.session.idle_timeout)
89 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
90 absolute_timeout: chrono::Duration::from_std(config.session.absolute_timeout)
91 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
92 max_concurrent_sessions: config.session.max_concurrent_sessions,
93 device_binding: config.session.device_binding,
94 ip_binding: config.session.ip_binding,
95 };
96
97 let sessions = Arc::new(SessionManager::new(session_config));
98
99 let api_keys = Arc::new(ApiKeyManager::new());
100
101 let risk_config = RiskConfig {
102 mfa_threshold: config.risk.mfa_threshold,
103 block_threshold: config.risk.block_threshold,
104 geo_velocity_enabled: config.risk.geo_velocity_check,
105 max_travel_speed_kmh: config.risk.max_travel_speed,
106 };
107
108 let risk = Arc::new(RiskEngine::new(risk_config));
109
110 let audit = Arc::new(AuditManager::new(90)); let crypto = Arc::new(CryptoManager::new());
113
114 Ok(Self {
115 config,
116 jwt,
117 password,
118 oauth2,
119 mfa,
120 permissions,
121 sessions,
122 api_keys,
123 risk,
124 audit,
125 crypto,
126 users: Arc::new(RwLock::new(HashMap::new())),
127 })
128 }
129
130 pub async fn register(&self, email: String, password: String) -> Result<Uuid> {
133 if !email.contains('@') {
135 return Err(AuthError::InvalidPassword("Invalid email format".to_string()));
136 }
137
138 let users = self.users.read().await;
140 if users.values().any(|u| u.email == email) {
141 return Err(AuthError::UserAlreadyExists(email));
142 }
143 drop(users);
144
145 let password_hash = self.password.hash_password(&password)?;
147
148 let user = User {
149 id: Uuid::new_v4(),
150 email: email.clone(),
151 email_verified: false,
152 password_hash,
153 display_name: None,
154 avatar_url: None,
155 roles: vec!["user".to_string()],
156 permissions: vec![],
157 metadata: HashMap::new(),
158 mfa_enabled: false,
159 mfa_secret: None,
160 webauthn_credentials: vec![],
161 created_at: Utc::now(),
162 updated_at: Utc::now(),
163 last_login_at: None,
164 login_count: 0,
165 failed_login_attempts: 0,
166 locked_until: None,
167 password_changed_at: Utc::now(),
168 status: UserStatus::Active,
169 };
170
171 let user_id = user.id;
172
173 let mut users = self.users.write().await;
174 users.insert(user_id, user);
175
176 self.audit.log(
177 Some(user_id),
178 None,
179 "user.register".to_string(),
180 "user".to_string(),
181 AuditResult::Success,
182 None,
183 None,
184 HashMap::new(),
185 0,
186 ).await;
187
188 tracing::info!(user_id = %user_id, email = %email, "User registered");
189
190 Ok(user_id)
191 }
192
193 pub async fn login(&self, credentials: Credentials) -> Result<Session> {
194 let users = self.users.read().await;
195
196 let user = users
197 .values()
198 .find(|u| u.email == credentials.email)
199 .cloned()
200 .ok_or(AuthError::InvalidCredentials)?;
201
202 drop(users);
203
204 if let Some(locked_until) = user.locked_until {
206 if Utc::now() < locked_until {
207 return Err(AuthError::AccountLocked(
208 format!("Account locked until {}", locked_until)
209 ));
210 }
211 }
212
213 let password_valid = self.password.verify_password(&credentials.password, &user.password_hash)?;
215
216 if !password_valid {
217 self.handle_failed_login(&user.id).await?;
218
219 self.audit.log(
220 Some(user.id),
221 None,
222 "user.login".to_string(),
223 "session".to_string(),
224 AuditResult::Failure,
225 credentials.ip_address,
226 None,
227 HashMap::new(),
228 50,
229 ).await;
230
231 return Err(AuthError::InvalidCredentials);
232 }
233
234 let risk_assessment = self.risk.assess_risk(
236 &user,
237 credentials.ip_address,
238 credentials.device_id.as_deref(),
239 None,
240 ).await?;
241
242 match risk_assessment.recommended_action {
243 RiskAction::Deny => {
244 self.audit.log(
245 Some(user.id),
246 None,
247 "user.login".to_string(),
248 "session".to_string(),
249 AuditResult::Blocked,
250 credentials.ip_address,
251 None,
252 HashMap::new(),
253 risk_assessment.score,
254 ).await;
255
256 return Err(AuthError::SuspiciousActivity(
257 "Login blocked due to high risk score".to_string()
258 ));
259 }
260 RiskAction::RequireMfa if !user.mfa_enabled => {
261 return Err(AuthError::MfaRequired);
262 }
263 _ => {}
264 }
265
266 self.risk.update_behavior_profile(
268 &user.id,
269 credentials.ip_address,
270 credentials.device_id.clone(),
271 true,
272 ).await;
273
274 let claims = self.jwt.create_claims(
276 user.id,
277 user.email.clone(),
278 user.roles.clone(),
279 user.permissions.clone(),
280 Uuid::new_v4(),
281 vec!["*".to_string()],
282 credentials.device_id.clone(),
283 );
284
285 let access_token = self.jwt.create_token(&claims).await?;
286 let refresh_token = self.crypto.generate_token(32);
287
288 let session = self.sessions.create_session(
290 user.id,
291 access_token,
292 refresh_token,
293 chrono::Duration::from_std(self.config.jwt.access_token_ttl)
294 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
295 chrono::Duration::from_std(self.config.jwt.refresh_token_ttl)
296 .map_err(|e| AuthError::ConfigError(e.to_string()))?,
297 credentials.device_id,
298 credentials.ip_address,
299 None,
300 vec!["*".to_string()],
301 ).await?;
302
303 let mut users = self.users.write().await;
305 if let Some(u) = users.get_mut(&user.id) {
306 u.last_login_at = Some(Utc::now());
307 u.login_count += 1;
308 u.failed_login_attempts = 0;
309 }
310
311 self.audit.log(
312 Some(user.id),
313 Some(session.id),
314 "user.login".to_string(),
315 "session".to_string(),
316 AuditResult::Success,
317 credentials.ip_address,
318 None,
319 HashMap::new(),
320 risk_assessment.score,
321 ).await;
322
323 Ok(session)
324 }
325
326 async fn handle_failed_login(&self, user_id: &Uuid) -> Result<()> {
327 let mut users = self.users.write().await;
328
329 if let Some(user) = users.get_mut(user_id) {
330 user.failed_login_attempts += 1;
331
332 if user.failed_login_attempts >= self.config.rate_limit.lockout_threshold {
333 user.locked_until = Some(Utc::now() + Duration::from_std(self.config.rate_limit.lockout_duration).unwrap());
334 tracing::warn!(user_id = %user_id, "Account locked due to failed login attempts");
335 }
336 }
337
338 Ok(())
339 }
340
341 pub async fn verify_token(&self, token: &str) -> Result<Claims> {
342 self.jwt.verify_token(token).await
343 }
344
345 pub async fn logout(&self, session_id: &Uuid) -> Result<()> {
346 self.sessions.delete_session(session_id).await?;
347
348 self.audit.log(
349 None,
350 Some(*session_id),
351 "user.logout".to_string(),
352 "session".to_string(),
353 AuditResult::Success,
354 None,
355 None,
356 HashMap::new(),
357 0,
358 ).await;
359
360 Ok(())
361 }
362
363 pub fn jwt_manager(&self) -> &JwtManager {
366 &self.jwt
367 }
368
369 pub fn password_manager(&self) -> &PasswordManager {
370 &self.password
371 }
372
373 pub fn oauth2_manager(&self) -> &OAuth2Manager {
374 &self.oauth2
375 }
376
377 pub fn mfa_manager(&self) -> &MfaManager {
378 &self.mfa
379 }
380
381 pub fn permission_manager(&self) -> &PermissionManager {
382 &self.permissions
383 }
384
385 pub fn session_manager(&self) -> &SessionManager {
386 &self.sessions
387 }
388
389 pub fn api_key_manager(&self) -> &ApiKeyManager {
390 &self.api_keys
391 }
392
393 pub fn risk_engine(&self) -> &RiskEngine {
394 &self.risk
395 }
396
397 pub fn audit_manager(&self) -> &AuditManager {
398 &self.audit
399 }
400
401 pub fn crypto_manager(&self) -> &CryptoManager {
402 &self.crypto
403 }
404}
405
406fn parse_algorithm(s: &str) -> std::result::Result<jsonwebtoken::Algorithm, String> {
408 match s {
409 "HS256" => Ok(jsonwebtoken::Algorithm::HS256),
410 "HS384" => Ok(jsonwebtoken::Algorithm::HS384),
411 "HS512" => Ok(jsonwebtoken::Algorithm::HS512),
412 "RS256" => Ok(jsonwebtoken::Algorithm::RS256),
413 "RS384" => Ok(jsonwebtoken::Algorithm::RS384),
414 "RS512" => Ok(jsonwebtoken::Algorithm::RS512),
415 "ES256" => Ok(jsonwebtoken::Algorithm::ES256),
416 "ES384" => Ok(jsonwebtoken::Algorithm::ES384),
417 _ => Err(format!("Unknown algorithm: {}", s)),
418 }
419}