firestore_db_and_auth/
users.rs

1//! # Firebase Auth API - User information
2//!
3//! Retrieve firebase user information
4
5use super::errors::{extract_google_api_error_async, Result};
6
7use super::sessions::{service_account, user};
8use serde::{Deserialize, Serialize};
9
10use crate::FirebaseAuthBearer;
11
12/// A federated services like Facebook, Github etc that the user has used to
13/// authenticated himself and that he associated with this firebase auth account.
14#[allow(non_snake_case)]
15#[derive(Debug, Default, Deserialize, Serialize)]
16pub struct ProviderUserInfo {
17    pub providerId: String,
18    pub federatedId: String,
19    pub displayName: Option<String>,
20    pub photoUrl: Option<String>,
21}
22
23/// Users id, email, display name and a few more information
24#[allow(non_snake_case)]
25#[derive(Debug, Default, Deserialize, Serialize)]
26pub struct FirebaseAuthUser {
27    pub localId: Option<String>,
28    pub email: Option<String>,
29    /// True if the user has verified his email address
30    pub emailVerified: Option<bool>,
31    pub displayName: Option<String>,
32    /// Find all federated services like Facebook, Github etc that the user has used to
33    /// authenticated himself and that he associated with this firebase auth account.
34    pub providerUserInfo: Option<Vec<ProviderUserInfo>>,
35    pub photoUrl: Option<String>,
36    /// True if the account is disabled. A disabled account cannot login anymore.
37    pub disabled: Option<bool>,
38    /// Last login datetime in UTC
39    pub lastLoginAt: Option<String>,
40    /// Created datetime in UTC
41    pub createdAt: Option<String>,
42    /// True if email/password login have been used
43    pub customAuth: Option<bool>,
44}
45
46/// Your user information query might return zero, one or more [`FirebaseAuthUser`] structures.
47#[derive(Debug, Default, Deserialize, Serialize)]
48pub struct FirebaseAuthUserResponse {
49    pub kind: String,
50    pub users: Vec<FirebaseAuthUser>,
51}
52
53#[allow(non_snake_case)]
54#[derive(Serialize)]
55struct UserRequest {
56    pub idToken: String,
57}
58
59#[inline]
60fn firebase_auth_url(v: &str, v2: &str) -> String {
61    format!("https://identitytoolkit.googleapis.com/v1/accounts:{}?key={}", v, v2)
62}
63
64/// Retrieve information about the firebase auth user associated with the given user session
65///
66/// Error codes:
67/// - INVALID_ID_TOKEN
68/// - USER_NOT_FOUND
69pub async fn user_info(session: &user::Session) -> Result<FirebaseAuthUserResponse> {
70    let url = firebase_auth_url("lookup", &session.api_key);
71
72    let resp = session
73        .client()
74        .post(&url)
75        .json(&UserRequest {
76            idToken: session.access_token().await,
77        })
78        .send()
79        .await?;
80
81    let resp = extract_google_api_error_async(resp, || session.user_id.to_owned()).await?;
82
83    Ok(resp.json().await?)
84}
85
86/// Removes the firebase auth user associated with the given user session
87///
88/// Error codes:
89/// - INVALID_ID_TOKEN
90/// - USER_NOT_FOUND
91pub async fn user_remove(session: &user::Session) -> Result<()> {
92    let url = firebase_auth_url("delete", &session.api_key);
93    let resp = session
94        .client()
95        .post(&url)
96        .json(&UserRequest {
97            idToken: session.access_token().await,
98        })
99        .send()
100        .await?;
101
102    extract_google_api_error_async(resp, || session.user_id.to_owned()).await?;
103    Ok({})
104}
105
106#[allow(non_snake_case)]
107#[derive(Default, Deserialize)]
108struct SignInUpUserResponse {
109    localId: String,
110    idToken: String,
111    refreshToken: String,
112}
113
114#[allow(non_snake_case)]
115#[derive(Serialize)]
116struct SignInUpUserRequest {
117    pub email: String,
118    pub password: String,
119    pub returnSecureToken: bool,
120}
121
122async fn sign_up_in(
123    session: &service_account::Session,
124    email: &str,
125    password: &str,
126    action: &str,
127) -> Result<user::Session> {
128    let url = firebase_auth_url(action, &session.credentials.api_key);
129    let resp = session
130        .client()
131        .post(&url)
132        .json(&SignInUpUserRequest {
133            email: email.to_owned(),
134            password: password.to_owned(),
135            returnSecureToken: true,
136        })
137        .send()
138        .await?;
139
140    let resp = extract_google_api_error_async(resp, || email.to_owned()).await?;
141
142    let resp: SignInUpUserResponse = resp.json().await?;
143
144    Ok(user::Session::new(
145        &session.credentials,
146        Some(&resp.localId),
147        Some(&resp.idToken),
148        Some(&resp.refreshToken),
149    )
150    .await?)
151}
152
153/// Creates the firebase auth user with the given email and password and returns
154/// a user session.
155///
156/// Error codes:
157/// EMAIL_EXISTS: The email address is already in use by another account.
158/// OPERATION_NOT_ALLOWED: Password sign-in is disabled for this project.
159/// TOO_MANY_ATTEMPTS_TRY_LATER: We have blocked all requests from this device due to unusual activity. Try again later.
160pub async fn sign_up(session: &service_account::Session, email: &str, password: &str) -> Result<user::Session> {
161    sign_up_in(session, email, password, "signUp").await
162}
163
164/// Signs in with the given email and password and returns a user session.
165///
166/// Error codes:
167/// EMAIL_NOT_FOUND: There is no user record corresponding to this identifier. The user may have been deleted.
168/// INVALID_PASSWORD: The password is invalid or the user does not have a password.
169/// USER_DISABLED: The user account has been disabled by an administrator.
170pub async fn sign_in(session: &service_account::Session, email: &str, password: &str) -> Result<user::Session> {
171    sign_up_in(session, email, password, "signInWithPassword").await
172}