firebase_rs_sdk/auth/
model.rs

1use crate::app::FirebaseApp;
2use crate::auth::error::{AuthError, AuthResult};
3use crate::auth::token_manager::{TokenManager, TokenUpdate};
4use crate::util::PartialObserver;
5use serde::{Deserialize, Serialize};
6use serde_json::json;
7use std::sync::{Arc, Mutex};
8use std::time::Duration;
9
10#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
11pub struct UserInfo {
12    pub uid: String,
13    pub display_name: Option<String>,
14    pub email: Option<String>,
15    pub phone_number: Option<String>,
16    pub photo_url: Option<String>,
17    pub provider_id: String,
18}
19
20#[derive(Clone, Debug)]
21pub struct User {
22    app: FirebaseApp,
23    info: UserInfo,
24    email_verified: bool,
25    is_anonymous: bool,
26    token_manager: TokenManager,
27}
28
29impl User {
30    /// Creates a new user bound to the given app with profile information.
31    pub fn new(app: FirebaseApp, info: UserInfo) -> Self {
32        Self {
33            app,
34            info,
35            email_verified: false,
36            is_anonymous: false,
37            token_manager: TokenManager::default(),
38        }
39    }
40
41    /// Returns the owning `FirebaseApp` for the user.
42    pub fn app(&self) -> &FirebaseApp {
43        &self.app
44    }
45
46    /// Indicates whether the user signed in anonymously.
47    pub fn is_anonymous(&self) -> bool {
48        self.is_anonymous
49    }
50
51    /// Flags the user as anonymous or regular.
52    pub fn set_anonymous(&mut self, anonymous: bool) {
53        self.is_anonymous = anonymous;
54    }
55
56    /// Returns the stable Firebase UID for the user.
57    pub fn uid(&self) -> &str {
58        &self.info.uid
59    }
60
61    /// Indicates whether the user's email has been verified.
62    pub fn email_verified(&self) -> bool {
63        self.email_verified
64    }
65
66    /// Returns the refresh token issued for this user, if present.
67    pub fn refresh_token(&self) -> Option<String> {
68        self.token_manager.refresh_token()
69    }
70
71    /// Returns the cached ID token or an error if none is available.
72    pub fn get_id_token(&self, _force_refresh: bool) -> AuthResult<String> {
73        self.token_manager
74            .access_token()
75            .ok_or_else(|| AuthError::InvalidCredential("Missing ID token".into()))
76    }
77
78    /// Exposes the underlying token manager.
79    pub fn token_manager(&self) -> &TokenManager {
80        &self.token_manager
81    }
82
83    /// Updates the cached tokens with fresh credentials from the backend.
84    pub fn update_tokens(
85        &self,
86        access_token: Option<String>,
87        refresh_token: Option<String>,
88        expires_in: Option<Duration>,
89    ) {
90        let update = TokenUpdate::new(access_token, refresh_token, expires_in);
91        self.token_manager.update(update);
92    }
93
94    /// Returns the immutable `UserInfo` profile snapshot.
95    pub fn info(&self) -> &UserInfo {
96        &self.info
97    }
98}
99
100#[derive(Clone, Debug)]
101pub struct UserCredential {
102    pub user: Arc<User>,
103    pub provider_id: Option<String>,
104    pub operation_type: Option<String>,
105}
106
107#[derive(Debug, Clone)]
108pub struct AuthCredential {
109    pub provider_id: String,
110    pub sign_in_method: String,
111    pub token_response: serde_json::Value,
112}
113
114#[derive(Debug, Clone, Default)]
115pub struct AuthConfig {
116    pub api_key: Option<String>,
117    pub identity_toolkit_endpoint: Option<String>,
118    pub secure_token_endpoint: Option<String>,
119}
120
121#[derive(Clone)]
122pub struct EmailAuthProvider;
123
124impl EmailAuthProvider {
125    pub const PROVIDER_ID: &'static str = "password";
126
127    /// Builds an auth credential suitable for email/password sign-in flows.
128    pub fn credential(email: &str, password: &str) -> AuthCredential {
129        AuthCredential {
130            provider_id: Self::PROVIDER_ID.to_string(),
131            sign_in_method: Self::PROVIDER_ID.to_string(),
132            token_response: json!({
133                "email": email,
134                "password": password,
135                "returnSecureToken": true,
136            }),
137        }
138    }
139}
140
141#[derive(Default)]
142pub struct AuthStateListeners {
143    observers: Mutex<Vec<PartialObserver<Arc<User>>>>,
144}
145
146impl AuthStateListeners {
147    /// Registers a new observer to receive auth state changes.
148    pub fn add_observer(&self, observer: PartialObserver<Arc<User>>) {
149        self.observers.lock().unwrap().push(observer);
150    }
151
152    /// Notifies all observers with the provided user snapshot.
153    pub fn notify(&self, user: Arc<User>) {
154        for observer in self.observers.lock().unwrap().iter() {
155            if let Some(next) = observer.next.clone() {
156                next(&user);
157            }
158        }
159    }
160}
161
162#[derive(Debug, Serialize)]
163pub struct SignInWithPasswordRequest {
164    pub email: String,
165    pub password: String,
166    #[serde(rename = "returnSecureToken")]
167    pub return_secure_token: bool,
168}
169
170#[derive(Debug, Deserialize)]
171pub struct SignInWithPasswordResponse {
172    #[serde(rename = "idToken")]
173    pub id_token: String,
174    #[serde(rename = "refreshToken")]
175    pub refresh_token: String,
176    #[serde(rename = "localId")]
177    pub local_id: String,
178    pub email: String,
179    #[serde(rename = "expiresIn")]
180    pub expires_in: String,
181}
182
183#[derive(Debug, Serialize)]
184pub struct SignUpRequest {
185    pub email: String,
186    pub password: String,
187    #[serde(rename = "returnSecureToken")]
188    pub return_secure_token: bool,
189}
190
191#[derive(Debug, Deserialize)]
192pub struct SignUpResponse {
193    #[serde(rename = "idToken")]
194    pub id_token: String,
195    #[serde(rename = "refreshToken")]
196    pub refresh_token: String,
197    #[serde(rename = "localId")]
198    pub local_id: String,
199    pub email: String,
200    #[serde(rename = "expiresIn")]
201    pub expires_in: Option<String>,
202}
203
204#[derive(Debug, Clone, Deserialize)]
205pub struct ProviderUserInfo {
206    #[serde(rename = "providerId")]
207    pub provider_id: Option<String>,
208    #[serde(rename = "rawId")]
209    pub raw_id: Option<String>,
210    #[serde(rename = "email")]
211    pub email: Option<String>,
212    #[serde(rename = "displayName")]
213    pub display_name: Option<String>,
214    #[serde(rename = "photoUrl")]
215    pub photo_url: Option<String>,
216    #[serde(rename = "phoneNumber")]
217    pub phone_number: Option<String>,
218}
219
220#[derive(Debug, Clone, Deserialize)]
221pub struct AccountInfoUser {
222    #[serde(rename = "localId")]
223    pub local_id: Option<String>,
224    #[serde(rename = "displayName")]
225    pub display_name: Option<String>,
226    #[serde(rename = "photoUrl")]
227    pub photo_url: Option<String>,
228    #[serde(rename = "email")]
229    pub email: Option<String>,
230    #[serde(rename = "emailVerified")]
231    pub email_verified: Option<bool>,
232    #[serde(rename = "phoneNumber")]
233    pub phone_number: Option<String>,
234    #[serde(rename = "providerUserInfo")]
235    pub provider_user_info: Option<Vec<ProviderUserInfo>>,
236}
237
238#[derive(Debug, Clone, Deserialize)]
239pub struct GetAccountInfoResponse {
240    pub users: Vec<AccountInfoUser>,
241}