1use crate::app::FirebaseApp;
2use crate::auth::error::{AuthError, AuthResult};
3use crate::auth::token_manager::{TokenManager, TokenUpdate};
4use crate::auth::types::MultiFactorInfo;
5use crate::util::PartialObserver;
6use serde::{Deserialize, Serialize};
7use serde_json::{json, Value};
8use std::sync::{Arc, Mutex};
9use std::time::Duration;
10
11#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
12pub struct UserInfo {
13 pub uid: String,
14 pub display_name: Option<String>,
15 pub email: Option<String>,
16 pub phone_number: Option<String>,
17 pub photo_url: Option<String>,
18 pub provider_id: String,
19}
20
21#[derive(Clone, Debug)]
22pub struct User {
23 app: FirebaseApp,
24 info: UserInfo,
25 email_verified: bool,
26 is_anonymous: bool,
27 token_manager: TokenManager,
28 mfa_factors: Arc<Mutex<Vec<MultiFactorInfo>>>,
29}
30
31impl User {
32 pub fn new(app: FirebaseApp, info: UserInfo) -> Self {
34 Self {
35 app,
36 info,
37 email_verified: false,
38 is_anonymous: false,
39 token_manager: TokenManager::default(),
40 mfa_factors: Arc::new(Mutex::new(Vec::new())),
41 }
42 }
43
44 pub fn app(&self) -> &FirebaseApp {
46 &self.app
47 }
48
49 pub fn is_anonymous(&self) -> bool {
51 self.is_anonymous
52 }
53
54 pub fn set_anonymous(&mut self, anonymous: bool) {
56 self.is_anonymous = anonymous;
57 }
58
59 pub fn uid(&self) -> &str {
61 &self.info.uid
62 }
63
64 pub fn email_verified(&self) -> bool {
66 self.email_verified
67 }
68
69 pub fn refresh_token(&self) -> Option<String> {
71 self.token_manager.refresh_token()
72 }
73
74 pub fn get_id_token(&self, _force_refresh: bool) -> AuthResult<String> {
76 self.token_manager
77 .access_token()
78 .ok_or_else(|| AuthError::InvalidCredential("Missing ID token".into()))
79 }
80
81 pub fn token_manager(&self) -> &TokenManager {
83 &self.token_manager
84 }
85
86 pub fn update_tokens(
88 &self,
89 access_token: Option<String>,
90 refresh_token: Option<String>,
91 expires_in: Option<Duration>,
92 ) {
93 let update = TokenUpdate::new(access_token, refresh_token, expires_in);
94 self.token_manager.update(update);
95 }
96
97 pub fn info(&self) -> &UserInfo {
99 &self.info
100 }
101
102 pub(crate) fn set_email_verified(&mut self, value: bool) {
103 self.email_verified = value;
104 }
105
106 pub(crate) fn set_mfa_info(&self, factors: Vec<MultiFactorInfo>) {
107 *self.mfa_factors.lock().unwrap() = factors;
108 }
109
110 pub fn mfa_info(&self) -> Vec<MultiFactorInfo> {
111 self.mfa_factors.lock().unwrap().clone()
112 }
113}
114
115#[derive(Clone, Debug)]
116pub struct UserCredential {
117 pub user: Arc<User>,
118 pub provider_id: Option<String>,
119 pub operation_type: Option<String>,
120}
121
122#[derive(Debug, Clone)]
123pub struct AuthCredential {
124 pub provider_id: String,
125 pub sign_in_method: String,
126 pub token_response: serde_json::Value,
127}
128
129impl AuthCredential {
130 pub fn to_json(&self) -> Value {
132 json!({
133 "providerId": self.provider_id,
134 "signInMethod": self.sign_in_method,
135 "tokenResponse": self.token_response,
136 })
137 }
138
139 pub fn to_json_string(&self) -> AuthResult<String> {
141 serde_json::to_string(&self.to_json())
142 .map_err(|err| AuthError::InvalidCredential(err.to_string()))
143 }
144
145 pub fn from_json(value: Value) -> AuthResult<Self> {
147 let provider_id = value
148 .get("providerId")
149 .and_then(Value::as_str)
150 .ok_or_else(|| {
151 AuthError::InvalidCredential("Credential JSON missing providerId".into())
152 })?
153 .to_string();
154
155 let sign_in_method = value
156 .get("signInMethod")
157 .and_then(Value::as_str)
158 .ok_or_else(|| {
159 AuthError::InvalidCredential("Credential JSON missing signInMethod".into())
160 })?
161 .to_string();
162
163 let token_response = value
164 .get("tokenResponse")
165 .cloned()
166 .unwrap_or_else(|| json!({}));
167
168 Ok(Self {
169 provider_id,
170 sign_in_method,
171 token_response,
172 })
173 }
174
175 pub fn from_json_str(data: &str) -> AuthResult<Self> {
177 let value: Value = serde_json::from_str(data)
178 .map_err(|err| AuthError::InvalidCredential(err.to_string()))?;
179 Self::from_json(value)
180 }
181}
182
183#[derive(Debug, Clone, Default)]
184pub struct AuthConfig {
185 pub api_key: Option<String>,
186 pub identity_toolkit_endpoint: Option<String>,
187 pub secure_token_endpoint: Option<String>,
188}
189
190#[derive(Clone)]
191pub struct EmailAuthProvider;
192
193impl EmailAuthProvider {
194 pub const PROVIDER_ID: &'static str = "password";
195
196 pub fn credential(email: &str, password: &str) -> AuthCredential {
198 AuthCredential {
199 provider_id: Self::PROVIDER_ID.to_string(),
200 sign_in_method: Self::PROVIDER_ID.to_string(),
201 token_response: json!({
202 "email": email,
203 "password": password,
204 "returnSecureToken": true,
205 }),
206 }
207 }
208}
209
210#[derive(Default)]
211pub struct AuthStateListeners {
212 observers: Mutex<Vec<PartialObserver<Arc<User>>>>,
213}
214
215impl AuthStateListeners {
216 pub fn add_observer(&self, observer: PartialObserver<Arc<User>>) {
218 self.observers.lock().unwrap().push(observer);
219 }
220
221 pub fn notify(&self, user: Arc<User>) {
223 for observer in self.observers.lock().unwrap().iter() {
224 if let Some(next) = observer.next.clone() {
225 next(&user);
226 }
227 }
228 }
229}
230
231#[derive(Debug, Serialize, Clone)]
232pub struct SignInWithPasswordRequest {
233 pub email: String,
234 pub password: String,
235 #[serde(rename = "returnSecureToken")]
236 pub return_secure_token: bool,
237}
238
239#[derive(Debug, Deserialize, Clone)]
240pub struct SignInWithPasswordResponse {
241 #[serde(rename = "idToken")]
242 pub id_token: Option<String>,
243 #[serde(rename = "refreshToken")]
244 pub refresh_token: Option<String>,
245 #[serde(rename = "localId")]
246 pub local_id: String,
247 pub email: String,
248 #[serde(rename = "expiresIn")]
249 pub expires_in: Option<String>,
250 #[serde(rename = "mfaPendingCredential")]
251 pub mfa_pending_credential: Option<String>,
252 #[serde(rename = "mfaInfo")]
253 pub mfa_info: Option<Vec<MfaEnrollmentInfo>>,
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259
260 #[test]
261 fn auth_credential_json_roundtrip() {
262 let credential = AuthCredential {
263 provider_id: "custom-provider".into(),
264 sign_in_method: "custom-provider".into(),
265 token_response: json!({ "idToken": "abc" }),
266 };
267
268 let json_value = credential.to_json();
269 let restored = AuthCredential::from_json(json_value.clone()).unwrap();
270 assert_eq!(restored.provider_id, "custom-provider");
271 assert_eq!(restored.sign_in_method, "custom-provider");
272 assert_eq!(restored.token_response, json_value["tokenResponse"]);
273
274 let json_string = credential.to_json_string().unwrap();
275 let restored_from_str = AuthCredential::from_json_str(&json_string).unwrap();
276 assert_eq!(restored_from_str.provider_id, "custom-provider");
277 assert_eq!(restored_from_str.sign_in_method, "custom-provider");
278 }
279}
280
281#[derive(Debug, Serialize, Clone, Default)]
282pub struct SignUpRequest {
283 #[serde(rename = "idToken", skip_serializing_if = "Option::is_none")]
284 pub id_token: Option<String>,
285 #[serde(rename = "returnSecureToken", skip_serializing_if = "Option::is_none")]
286 pub return_secure_token: Option<bool>,
287 #[serde(rename = "email", skip_serializing_if = "Option::is_none")]
288 pub email: Option<String>,
289 #[serde(rename = "password", skip_serializing_if = "Option::is_none")]
290 pub password: Option<String>,
291 #[serde(rename = "tenantId", skip_serializing_if = "Option::is_none")]
292 pub tenant_id: Option<String>,
293 #[serde(rename = "captchaResponse", skip_serializing_if = "Option::is_none")]
294 pub captcha_response: Option<String>,
295 #[serde(rename = "clientType", skip_serializing_if = "Option::is_none")]
296 pub client_type: Option<String>,
297 #[serde(rename = "recaptchaVersion", skip_serializing_if = "Option::is_none")]
298 pub recaptcha_version: Option<String>,
299}
300
301#[derive(Debug, Deserialize, Clone)]
302pub struct SignUpResponse {
303 #[serde(rename = "idToken")]
304 pub id_token: Option<String>,
305 #[serde(rename = "refreshToken")]
306 pub refresh_token: Option<String>,
307 #[serde(rename = "localId")]
308 pub local_id: Option<String>,
309 #[serde(rename = "email")]
310 pub email: Option<String>,
311 #[serde(rename = "displayName")]
312 pub display_name: Option<String>,
313 #[serde(rename = "expiresIn")]
314 pub expires_in: Option<String>,
315 #[serde(rename = "isNewUser")]
316 pub is_new_user: Option<bool>,
317 #[serde(rename = "mfaPendingCredential")]
318 pub mfa_pending_credential: Option<String>,
319 #[serde(rename = "mfaInfo")]
320 pub mfa_info: Option<Vec<MfaEnrollmentInfo>>,
321}
322
323#[derive(Debug, Serialize, Clone)]
324pub struct SignInWithCustomTokenRequest {
325 #[serde(rename = "token")]
326 pub token: String,
327 #[serde(rename = "returnSecureToken")]
328 pub return_secure_token: bool,
329}
330
331#[derive(Debug, Deserialize, Clone)]
332pub struct SignInWithCustomTokenResponse {
333 #[serde(rename = "idToken")]
334 pub id_token: Option<String>,
335 #[serde(rename = "refreshToken")]
336 pub refresh_token: Option<String>,
337 #[serde(rename = "localId")]
338 pub local_id: Option<String>,
339 #[serde(rename = "email")]
340 pub email: Option<String>,
341 #[serde(rename = "expiresIn")]
342 pub expires_in: Option<String>,
343 #[serde(rename = "isNewUser")]
344 pub is_new_user: Option<bool>,
345 #[serde(rename = "mfaPendingCredential")]
346 pub mfa_pending_credential: Option<String>,
347 #[serde(rename = "mfaInfo")]
348 pub mfa_info: Option<Vec<MfaEnrollmentInfo>>,
349}
350
351#[derive(Debug, Serialize, Clone)]
352pub struct SignInWithEmailLinkRequest {
353 #[serde(rename = "email")]
354 pub email: String,
355 #[serde(rename = "oobCode")]
356 pub oob_code: String,
357 #[serde(rename = "returnSecureToken")]
358 pub return_secure_token: bool,
359 #[serde(rename = "tenantId", skip_serializing_if = "Option::is_none")]
360 pub tenant_id: Option<String>,
361 #[serde(rename = "idToken", skip_serializing_if = "Option::is_none")]
362 pub id_token: Option<String>,
363}
364
365#[derive(Debug, Deserialize, Clone)]
366pub struct SignInWithEmailLinkResponse {
367 #[serde(rename = "idToken")]
368 pub id_token: Option<String>,
369 #[serde(rename = "refreshToken")]
370 pub refresh_token: Option<String>,
371 #[serde(rename = "localId")]
372 pub local_id: Option<String>,
373 #[serde(rename = "email")]
374 pub email: Option<String>,
375 #[serde(rename = "expiresIn")]
376 pub expires_in: Option<String>,
377 #[serde(rename = "isNewUser")]
378 pub is_new_user: Option<bool>,
379 #[serde(rename = "mfaPendingCredential")]
380 pub mfa_pending_credential: Option<String>,
381 #[serde(rename = "mfaInfo")]
382 pub mfa_info: Option<Vec<MfaEnrollmentInfo>>,
383}
384
385#[derive(Debug, Clone, Deserialize)]
386pub struct ProviderUserInfo {
387 #[serde(rename = "providerId")]
388 pub provider_id: Option<String>,
389 #[serde(rename = "rawId")]
390 pub raw_id: Option<String>,
391 #[serde(rename = "email")]
392 pub email: Option<String>,
393 #[serde(rename = "displayName")]
394 pub display_name: Option<String>,
395 #[serde(rename = "photoUrl")]
396 pub photo_url: Option<String>,
397 #[serde(rename = "phoneNumber")]
398 pub phone_number: Option<String>,
399}
400
401#[derive(Debug, Clone, Deserialize, Default)]
402pub struct MfaEnrollmentInfo {
403 #[serde(rename = "mfaEnrollmentId")]
404 pub mfa_enrollment_id: Option<String>,
405 #[serde(rename = "displayName")]
406 pub display_name: Option<String>,
407 #[serde(rename = "phoneInfo")]
408 pub phone_info: Option<String>,
409 #[serde(rename = "totpInfo")]
410 pub totp_info: Option<Value>,
411 #[serde(rename = "webauthnInfo")]
412 pub webauthn_info: Option<Value>,
413 #[serde(rename = "enrolledAt")]
414 pub enrolled_at: Option<Value>,
415 #[serde(rename = "factorId")]
416 pub factor_id: Option<String>,
417}
418
419#[derive(Debug, Clone, Deserialize)]
420pub struct AccountInfoUser {
421 #[serde(rename = "localId")]
422 pub local_id: Option<String>,
423 #[serde(rename = "displayName")]
424 pub display_name: Option<String>,
425 #[serde(rename = "photoUrl")]
426 pub photo_url: Option<String>,
427 #[serde(rename = "email")]
428 pub email: Option<String>,
429 #[serde(rename = "emailVerified")]
430 pub email_verified: Option<bool>,
431 #[serde(rename = "phoneNumber")]
432 pub phone_number: Option<String>,
433 #[serde(rename = "providerUserInfo")]
434 pub provider_user_info: Option<Vec<ProviderUserInfo>>,
435 #[serde(rename = "mfaInfo")]
436 pub mfa_info: Option<Vec<MfaEnrollmentInfo>>,
437}
438
439#[derive(Debug, Clone, Deserialize)]
440pub struct GetAccountInfoResponse {
441 pub users: Vec<AccountInfoUser>,
442}