1use crate::{Authenticator, AuthError, AuthProvider, AuthResult, UserProfile, generate_offline_uuid};
7
8#[cfg(feature = "events")]
9use lighty_event::{EventBus, Event, AuthEvent};
10
11pub struct OfflineAuth {
13 username: String,
14}
15
16impl OfflineAuth {
17 pub fn new(username: impl Into<String>) -> Self {
19 Self {
20 username: username.into(),
21 }
22 }
23
24 pub fn username(&self) -> &str {
26 &self.username
27 }
28}
29
30impl Authenticator for OfflineAuth {
31 async fn authenticate(
32 &mut self,
33 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
34 ) -> AuthResult<UserProfile> {
35 #[cfg(feature = "events")]
36 if let Some(bus) = event_bus {
37 bus.emit(Event::Auth(AuthEvent::AuthenticationStarted {
38 provider: "Offline".to_string(),
39 }));
40 }
41
42 if self.username.is_empty() {
43 #[cfg(feature = "events")]
44 if let Some(bus) = event_bus {
45 bus.emit(Event::Auth(AuthEvent::AuthenticationFailed {
46 provider: "Offline".to_string(),
47 error: "Username cannot be empty".to_string(),
48 }));
49 }
50 return Err(AuthError::InvalidCredentials);
51 }
52
53 if self.username.len() < 3 || self.username.len() > 16 {
54 return Err(rejected(
55 AuthError::UsernameLength { min: 3, max: 16 },
56 #[cfg(feature = "events")]
57 event_bus,
58 ));
59 }
60
61 if !self.username.chars().all(|c| c.is_alphanumeric() || c == '_') {
62 return Err(rejected(
63 AuthError::UsernameCharset,
64 #[cfg(feature = "events")]
65 event_bus,
66 ));
67 }
68
69 let uuid = generate_offline_uuid(&self.username);
70
71 #[cfg(feature = "events")]
72 if let Some(bus) = event_bus {
73 bus.emit(Event::Auth(AuthEvent::AuthenticationSuccess {
74 provider: "Offline".to_string(),
75 username: self.username.clone(),
76 uuid: uuid.clone(),
77 }));
78 }
79
80 Ok(UserProfile {
81 id: None,
82 username: self.username.clone(),
83 uuid,
84 access_token: None,
85 #[cfg(feature = "keyring")]
86 token_handle: None,
87 xuid: None,
88 email: None,
89 email_verified: false,
90 money: None,
91 role: None,
92 banned: false,
93 provider: AuthProvider::Offline,
94 })
95 }
96}
97
98fn rejected(
101 error: AuthError,
102 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
103) -> AuthError {
104 #[cfg(feature = "events")]
105 if let Some(bus) = event_bus {
106 bus.emit(Event::Auth(AuthEvent::AuthenticationFailed {
107 provider: "Offline".to_string(),
108 error: error.to_string(),
109 }));
110 }
111
112 error
113}