firebase-user 0.5.0

Manage Firebase users
Documentation
use serde::{Deserialize, Serialize};

use crate::{
    error::{ApiError, check_response},
    url::FirebaseUrl,
};

pub async fn get(
    firebase_url: &FirebaseUrl,
    id_token: &str,
) -> Result<Option<GetAccountResponse>, ApiError> {
    let client = reqwest::Client::new();

    let response = client
        .post(&firebase_url.account_lookup_url)
        .header("Content-Type", "application/json")
        .json(&GetAccountPayload { id_token })
        .send()
        .await?;

    let mut body: GetAccountResponseOuter = check_response(response).await?;

    Ok(body.users.pop())
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct GetAccountPayload<'a> {
    id_token: &'a str,
}

#[derive(Debug, Deserialize)]
struct GetAccountResponseOuter {
    users: Vec<GetAccountResponse>,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetAccountResponse {
    pub local_id: String,
    pub email: String,
    pub password_hash: String,
    pub email_verified: bool,
    pub password_updated_at: u64,
    pub provider_user_info: Vec<ProviderUserInfo>,
    pub valid_since: String,
    pub last_login_at: String,
    pub created_at: String,
    pub last_refresh_at: String,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ProviderUserInfo {
    pub provider_id: String,
    pub federated_id: String,
    pub email: String,
    pub raw_id: String,
}

pub async fn update(
    firebase_url: &FirebaseUrl,
    id_token: &str,
    email: Option<&str>,
    password: Option<&str>,
    return_secure_token: bool,
) -> Result<UpdateAccountResponse, ApiError> {
    let client = reqwest::Client::new();

    let response = client
        .post(&firebase_url.account_update_url)
        .header("Content-Type", "application/json")
        .json(&UpdateAccountPayload {
            id_token,
            email,
            password,
            return_secure_token,
        })
        .send()
        .await?;

    check_response(response).await
}

pub async fn change_email(
    firebase_url: &FirebaseUrl,
    id_token: &str,
    email: &str,
    return_secure_token: bool,
) -> Result<UpdateAccountResponse, ApiError> {
    update(
        firebase_url,
        id_token,
        Some(email),
        None,
        return_secure_token,
    )
    .await
}

pub async fn change_password(
    firebase_url: &FirebaseUrl,
    id_token: &str,
    password: &str,
    return_secure_token: bool,
) -> Result<UpdateAccountResponse, ApiError> {
    update(
        firebase_url,
        id_token,
        None,
        Some(password),
        return_secure_token,
    )
    .await
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct UpdateAccountPayload<'a> {
    id_token: &'a str,
    #[serde(skip_serializing_if = "Option::is_none")]
    email: Option<&'a str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    password: Option<&'a str>,
    return_secure_token: bool,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UpdateAccountResponse {
    pub kind: String,
    pub local_id: String,
    pub email: String,
    pub provider_user_info: Vec<ProviderUserInfo>,
    pub password_hash: String,
    pub email_verified: bool,
    pub id_token: Option<String>,
    pub refresh_token: Option<String>,
    pub expires_in: Option<String>,
}

pub async fn send_oob_code(
    firebase_url: &FirebaseUrl,
    request_type: &str,
    id_token: Option<&str>,
    email: Option<&str>,
) -> Result<SendOobCodeResponse, ApiError> {
    let client = reqwest::Client::new();

    let response = client
        .post(&firebase_url.account_send_oob_code_url)
        .header("Content-Type", "application/json")
        .json(&SendOobCodePayload {
            request_type,
            id_token,
            email,
        })
        .send()
        .await?;

    check_response(response).await
}

pub async fn reset_password(
    firebase_url: &FirebaseUrl,
    email: &str,
) -> Result<SendOobCodeResponse, ApiError> {
    send_oob_code(firebase_url, "PASSWORD_RESET", None, Some(email)).await
}

pub async fn verify_email(
    firebase_url: &FirebaseUrl,
    id_token: &str,
) -> Result<SendOobCodeResponse, ApiError> {
    send_oob_code(firebase_url, "VERIFY_EMAIL", Some(id_token), None).await
}

#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct SendOobCodePayload<'a> {
    request_type: &'a str,
    id_token: Option<&'a str>,
    email: Option<&'a str>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct SendOobCodeResponse {
    pub kind: String,
    pub email: String,
}