mecha10_auth/
types.rs

1//! Authentication type definitions
2//!
3//! Types for storing and managing user credentials and device code flow.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7
8/// Stored credentials for authenticated user
9///
10/// Saved to ~/.mecha10/credentials.json after successful login.
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct Credentials {
13    /// User's API key for authenticating with control plane
14    pub api_key: String,
15
16    /// User ID from the auth system
17    pub user_id: String,
18
19    /// User's email address
20    pub email: String,
21
22    /// User's display name
23    #[serde(default)]
24    pub name: Option<String>,
25
26    /// When the credentials were obtained
27    pub authenticated_at: DateTime<Utc>,
28
29    /// The auth server URL used for authentication
30    pub auth_url: String,
31}
32
33impl Credentials {
34    /// Check if credentials appear valid (non-empty api_key)
35    pub fn is_valid(&self) -> bool {
36        !self.api_key.is_empty() && self.api_key.starts_with("mecha_")
37    }
38
39    /// Get a masked version of the API key for display
40    pub fn masked_api_key(&self) -> String {
41        if self.api_key.len() > 12 {
42            format!("{}...{}", &self.api_key[..12], &self.api_key[self.api_key.len() - 4..])
43        } else {
44            "***".to_string()
45        }
46    }
47
48    /// Get display name (name if available, otherwise email)
49    pub fn display_name(&self) -> &str {
50        self.name.as_deref().unwrap_or(&self.email)
51    }
52}
53
54/// Response from device code request
55///
56/// Returned by POST /auth/device/code
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct DeviceCodeResponse {
59    /// The device code for backend polling
60    pub device_code: String,
61
62    /// User-facing code to enter in browser
63    pub user_code: String,
64
65    /// URL for user to visit
66    pub verification_uri: String,
67
68    /// Seconds until the code expires
69    pub expires_in: u32,
70
71    /// Seconds to wait between poll attempts
72    pub interval: u32,
73}
74
75/// Status of a device code authorization
76#[derive(Debug, Clone, Serialize, Deserialize)]
77#[serde(tag = "status", rename_all = "snake_case")]
78pub enum DeviceCodeStatus {
79    /// User hasn't completed authorization yet
80    Pending,
81
82    /// User authorized - includes credentials
83    Authorized {
84        api_key: String,
85        user_id: String,
86        email: String,
87        name: Option<String>,
88    },
89
90    /// User denied the authorization
91    Denied,
92
93    /// Device code has expired
94    Expired,
95}
96
97impl DeviceCodeStatus {
98    /// Check if this is a terminal state (no more polling needed)
99    pub fn is_terminal(&self) -> bool {
100        !matches!(self, DeviceCodeStatus::Pending)
101    }
102
103    /// Check if authorization was successful
104    pub fn is_authorized(&self) -> bool {
105        matches!(self, DeviceCodeStatus::Authorized { .. })
106    }
107}
108
109/// Error types for authentication operations
110#[derive(Debug, Clone, Serialize, Deserialize, thiserror::Error)]
111#[serde(tag = "error", rename_all = "snake_case")]
112pub enum AuthError {
113    /// Network or connection error
114    #[error("Network error: {message}")]
115    NetworkError { message: String },
116
117    /// Server returned an error
118    #[error("Server error ({status_code:?}): {message}")]
119    ServerError { message: String, status_code: Option<u16> },
120
121    /// Device code expired before user completed auth
122    #[error("Device code expired")]
123    ExpiredCode,
124
125    /// User denied the authorization request
126    #[error("Access denied by user")]
127    AccessDenied,
128
129    /// Credentials file corrupted or invalid
130    #[error("Invalid credentials: {message}")]
131    InvalidCredentials { message: String },
132
133    /// Rate limited by server
134    #[error("Rate limited, retry after {retry_after:?} seconds")]
135    RateLimited { retry_after: Option<u32> },
136}