server/auth/
auth_provider.rs

1use super::auth_state::{AuthStateManager, AuthenticationState};
2use super::provider::{AuthProvider as AuthProviderTrait, AuthToken};
3use super::types::AuthType;
4use crate::service_bus_manager::ServiceBusError;
5use async_trait::async_trait;
6use std::sync::Arc;
7
8/// Authentication provider that integrates with UI authentication state.
9///
10/// Provides a bridge between the server-side authentication system and the UI
11/// authentication state, enabling seamless authentication flow coordination between
12/// the terminal interface and Azure Service Bus operations.
13///
14/// This provider prioritizes UI-managed authentication tokens and provides fallback
15/// mechanisms for automated scenarios where UI authentication is not available.
16///
17/// See also: [`AuthStateManager`] for state management, [`AuthProvider`](crate::auth::provider::AuthProvider) for the base trait.
18///
19/// # Architecture
20///
21/// The [`AuthProvider`] implements a hierarchical authentication strategy:
22///
23/// 1. **UI State Priority** - First checks for valid tokens from UI authentication
24/// 2. **State-based Authentication** - Uses centralized authentication state management
25/// 3. **Fallback Provider** - Falls back to alternative authentication methods if available
26/// 4. **Error Propagation** - Provides detailed error feedback for authentication failures
27///
28/// # Authentication Flow
29///
30/// ```no_run
31/// use quetty_server::auth::{AuthProvider, AuthStateManager};
32/// use std::sync::Arc;
33///
34/// // Create with UI authentication state integration
35/// let auth_state = Arc::new(AuthStateManager::new());
36/// let provider = AuthProvider::new(auth_state, None);
37///
38/// // Authenticate using UI state or fallback methods
39/// match provider.authenticate().await {
40///     Ok(token) => {
41///         println!("Authentication successful: {}", token.token);
42///         // Use token for Service Bus operations
43///     }
44///     Err(e) => eprintln!("Authentication failed: {}", e),
45/// }
46/// ```
47///
48/// # Integration with UI Authentication
49///
50/// This provider seamlessly integrates with UI authentication flows:
51///
52/// - **Device Code Flow** - Coordinates with UI device code authentication
53/// - **Token Management** - Uses UI-managed token cache and refresh logic
54/// - **State Synchronization** - Maintains consistency between UI and server authentication
55/// - **Error Handling** - Provides user-friendly error messages for UI display
56///
57/// # Fallback Authentication
58///
59/// When UI authentication is not available, the provider can use fallback methods:
60///
61/// For more details on fallback providers, see [`ConnectionStringProvider`].
62///
63/// ```no_run
64/// use quetty_server::auth::{AuthProvider, AuthStateManager, ConnectionStringProvider};
65/// use std::sync::Arc;
66///
67/// // Create fallback provider for automated scenarios
68/// let connection_provider = Arc::new(ConnectionStringProvider::new(config)?);
69/// let auth_state = Arc::new(AuthStateManager::new());
70///
71/// let provider = AuthProvider::new(
72///     auth_state,
73///     Some(connection_provider as Arc<dyn AuthProviderTrait>)
74/// );
75///
76/// // Will use UI state if available, otherwise fall back to connection string
77/// let token = provider.authenticate().await?;
78/// ```
79///
80/// # Thread Safety
81///
82/// The provider is designed for concurrent access and can be safely shared across
83/// multiple threads and async tasks. All internal state is protected by appropriate
84/// synchronization mechanisms.
85///
86/// # Error Handling
87///
88/// Provides comprehensive error handling for various authentication scenarios:
89///
90/// - **Not Authenticated** - Clear guidance for users to authenticate through UI
91/// - **Authentication in Progress** - Informative messages during device code flow
92/// - **Authentication Failed** - Detailed error information from underlying providers
93///
94/// All errors are returned as [`ServiceBusError`](crate::service_bus_manager::ServiceBusError) variants.
95/// - **Token Refresh Failures** - Graceful handling of token expiration scenarios
96pub struct AuthProvider {
97    /// Centralized authentication state manager shared with UI components
98    auth_state: Arc<AuthStateManager>,
99    /// Optional fallback authentication provider for automated scenarios
100    fallback_provider: Option<Arc<dyn AuthProviderTrait>>,
101}
102
103impl AuthProvider {
104    /// Creates a new authentication provider with UI state integration.
105    ///
106    /// # Arguments
107    ///
108    /// * `auth_state` - Shared authentication state manager for UI coordination
109    /// * `fallback_provider` - Optional fallback provider for automated scenarios
110    ///
111    /// # Returns
112    ///
113    /// A new `AuthProvider` instance ready for authentication operations
114    ///
115    /// # Examples
116    ///
117    /// ```no_run
118    /// use quetty_server::auth::{AuthProvider, AuthStateManager};
119    /// use std::sync::Arc;
120    ///
121    /// // Basic provider with UI state only
122    /// let auth_state = Arc::new(AuthStateManager::new());
123    /// let provider = AuthProvider::new(auth_state, None);
124    ///
125    /// // Provider with fallback for automated scenarios
126    /// let auth_state = Arc::new(AuthStateManager::new());
127    /// let fallback = Arc::new(connection_provider);
128    /// let provider = AuthProvider::new(auth_state, Some(fallback));
129    /// ```
130    pub fn new(
131        auth_state: Arc<AuthStateManager>,
132        fallback_provider: Option<Arc<dyn AuthProviderTrait>>,
133    ) -> Self {
134        Self {
135            auth_state,
136            fallback_provider,
137        }
138    }
139}
140
141#[async_trait]
142impl AuthProviderTrait for AuthProvider {
143    async fn authenticate(&self) -> Result<AuthToken, ServiceBusError> {
144        // First check if we have a valid token from UI authentication
145        if let Some(token) = self.auth_state.get_azure_ad_token().await {
146            return Ok(AuthToken {
147                token,
148                token_type: "Bearer".to_string(),
149                expires_in_secs: Some(3600), // Default 1 hour
150            });
151        }
152
153        // Check the authentication state
154        match self.auth_state.get_state().await {
155            AuthenticationState::Authenticated { token, .. } => {
156                Ok(AuthToken {
157                    token,
158                    token_type: "Bearer".to_string(),
159                    expires_in_secs: Some(3600), // Default 1 hour
160                })
161            }
162            AuthenticationState::AwaitingDeviceCode { .. } => {
163                Err(ServiceBusError::AuthenticationError(
164                    "Authentication in progress. Please complete device code authentication in the UI.".to_string()
165                ))
166            }
167            AuthenticationState::Failed(error) => {
168                Err(ServiceBusError::AuthenticationError(
169                    format!("Authentication failed: {error}")
170                ))
171            }
172            AuthenticationState::NotAuthenticated => {
173                // If we have a fallback provider, try it
174                if let Some(fallback) = &self.fallback_provider {
175                    fallback.authenticate().await
176                } else {
177                    Err(ServiceBusError::AuthenticationError(
178                        "Not authenticated. Please authenticate through the UI first.".to_string()
179                    ))
180                }
181            }
182        }
183    }
184
185    async fn refresh(&self) -> Result<AuthToken, ServiceBusError> {
186        // For now, just try to authenticate again
187        self.authenticate().await
188    }
189
190    fn auth_type(&self) -> AuthType {
191        AuthType::AzureAd
192    }
193
194    fn requires_refresh(&self) -> bool {
195        // Let the auth state manager handle refresh logic
196        false
197    }
198}