1use crate::{Authenticator, AuthError, AuthResult, UserProfile, generate_offline_uuid};
10
11#[cfg(feature = "events")]
12use lighty_event::{EventBus, Event, AuthEvent};
13
14pub struct OfflineAuth {
32 username: String,
33}
34
35impl OfflineAuth {
36 pub fn new(username: impl Into<String>) -> Self {
44 Self {
45 username: username.into(),
46 }
47 }
48
49 pub fn username(&self) -> &str {
51 &self.username
52 }
53}
54
55impl Authenticator for OfflineAuth {
56 async fn authenticate(
57 &mut self,
58 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
59 ) -> AuthResult<UserProfile> {
60 #[cfg(feature = "events")]
62 if let Some(bus) = event_bus {
63 bus.emit(Event::Auth(AuthEvent::AuthenticationStarted {
64 provider: "Offline".to_string(),
65 }));
66 }
67
68 if self.username.is_empty() {
70 #[cfg(feature = "events")]
71 if let Some(bus) = event_bus {
72 bus.emit(Event::Auth(AuthEvent::AuthenticationFailed {
73 provider: "Offline".to_string(),
74 error: "Username cannot be empty".to_string(),
75 }));
76 }
77 return Err(AuthError::InvalidCredentials);
78 }
79
80 if self.username.len() < 3 || self.username.len() > 16 {
81 let error_msg = "Username must be between 3 and 16 characters".to_string();
82 #[cfg(feature = "events")]
83 if let Some(bus) = event_bus {
84 bus.emit(Event::Auth(AuthEvent::AuthenticationFailed {
85 provider: "Offline".to_string(),
86 error: error_msg.clone(),
87 }));
88 }
89 return Err(AuthError::Custom(error_msg));
90 }
91
92 if !self.username.chars().all(|c| c.is_alphanumeric() || c == '_') {
94 let error_msg = "Username can only contain letters, numbers, and underscores".to_string();
95 #[cfg(feature = "events")]
96 if let Some(bus) = event_bus {
97 bus.emit(Event::Auth(AuthEvent::AuthenticationFailed {
98 provider: "Offline".to_string(),
99 error: error_msg.clone(),
100 }));
101 }
102 return Err(AuthError::Custom(error_msg));
103 }
104
105 let uuid = generate_offline_uuid(&self.username);
107
108 #[cfg(feature = "events")]
110 if let Some(bus) = event_bus {
111 bus.emit(Event::Auth(AuthEvent::AuthenticationSuccess {
112 provider: "Offline".to_string(),
113 username: self.username.clone(),
114 uuid: uuid.clone(),
115 }));
116 }
117
118 Ok(UserProfile {
119 id: None,
120 username: self.username.clone(),
121 uuid,
122 access_token: None,
123 email: None,
124 email_verified: false,
125 money: None,
126 role: None,
127 banned: false,
128 })
129 }
130}