lighty_auth/
auth.rs

1use std::future::Future;
2use serde::{Deserialize, Serialize};
3use crate::AuthError;
4
5#[cfg(feature = "events")]
6use lighty_event::EventBus;
7
8pub type AuthResult<T> = Result<T, AuthError>;
9
10/// User profile returned after successful authentication
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct UserProfile {
13    /// User ID (optional for offline mode)
14    pub id: Option<u64>,
15
16    /// Username
17    pub username: String,
18
19    /// Minecraft UUID (with dashes)
20    pub uuid: String,
21
22    /// Access token for session validation
23    pub access_token: Option<String>,
24
25    /// User email (optional)
26    pub email: Option<String>,
27
28    /// Email verification status
29    pub email_verified: bool,
30
31    /// User money/credits (for custom launchers)
32    pub money: Option<f64>,
33
34    /// User role/rank
35    pub role: Option<UserRole>,
36
37    /// Whether the account is banned
38    pub banned: bool,
39}
40
41/// User role/rank information
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct UserRole {
44    /// Role name
45    pub name: String,
46
47    /// Role color (hex format: #RRGGBB)
48    pub color: Option<String>,
49}
50
51/// Authentication provider type
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub enum AuthProvider {
54    /// Offline mode - no authentication
55    Offline,
56
57    /// Azuriom CMS authentication
58    Azuriom {
59        /// Base URL of the Azuriom instance (e.g., "https://example.com")
60        base_url: String,
61    },
62
63    /// Microsoft/Xbox Live authentication
64    Microsoft {
65        /// OAuth client ID
66        client_id: String,
67    },
68
69    /// Custom authentication endpoint
70    Custom {
71        /// Base URL of the custom auth API
72        base_url: String,
73    },
74}
75
76/// Core authentication trait
77///
78/// All authentication providers must implement this trait
79pub trait Authenticator {
80    /// Authenticate a user and return their profile
81    ///
82    /// # Arguments
83    /// - `event_bus`: Optional event bus for emitting auth events
84    ///
85    /// # Returns
86    /// - `Ok(UserProfile)` on success
87    /// - `Err(AuthError)` on failure
88    fn authenticate(
89        &mut self,
90        #[cfg(feature = "events")] event_bus: Option<&EventBus>,
91    ) -> impl Future<Output = AuthResult<UserProfile>> + Send;
92
93    /// Verify if a token is still valid
94    ///
95    /// # Arguments
96    /// - `token`: The access token to verify
97    ///
98    /// # Returns
99    /// - `Ok(UserProfile)` if token is valid
100    /// - `Err(AuthError)` if token is invalid or expired
101    fn verify(&self, token: &str) -> impl Future<Output = AuthResult<UserProfile>> + Send {
102        async move {
103            let _ = token;
104            Err(AuthError::Custom("Verification not supported for this provider".into()))
105        }
106    }
107
108    /// Logout and invalidate the token
109    ///
110    /// # Arguments
111    /// - `token`: The access token to invalidate
112    fn logout(&self, token: &str) -> impl Future<Output = AuthResult<()>> + Send {
113        async move {
114            let _ = token;
115            Ok(())
116        }
117    }
118}
119
120/// Helper to generate UUID v5 from username (for offline mode)
121///
122/// Uses SHA1 hashing to generate a deterministic UUID from the username.
123/// This ensures the same username always produces the same UUID.
124///
125/// # Arguments
126/// - `username`: The username to generate a UUID for
127///
128/// # Returns
129/// A UUID v5 string in the format: xxxxxxxx-xxxx-5xxx-yxxx-xxxxxxxxxxxx
130pub fn generate_offline_uuid(username: &str) -> String {
131    // Namespace for offline UUIDs (OfflinePlayer)
132    const NAMESPACE: &[u8] = b"OfflinePlayer:";
133
134    // Concatenate namespace and username
135    let mut data = Vec::with_capacity(NAMESPACE.len() + username.len());
136    data.extend_from_slice(NAMESPACE);
137    data.extend_from_slice(username.as_bytes());
138
139    // Calculate SHA1 hash using lighty-core
140    let hash = lighty_core::calculate_sha1_bytes_raw(&data);
141
142    // Format as UUID v5 (SHA1-based)
143    // Version bits: 0101 (5) in the 13th position
144    // Variant bits: 10xx in the 17th position (RFC 4122)
145    format!(
146        "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-5{:01x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
147        hash[0], hash[1], hash[2], hash[3],
148        hash[4], hash[5],
149        hash[6] & 0x0f, hash[7],
150        (hash[8] & 0x3f) | 0x80, hash[9],
151        hash[10], hash[11], hash[12], hash[13], hash[14], hash[15]
152    )
153}