firebase_rs_sdk/installations/rest/
mod.rs

1use std::time::{Duration, SystemTime};
2
3use serde::{Deserialize, Serialize};
4
5use crate::installations::error::{invalid_argument, InstallationsResult};
6use crate::installations::types::InstallationToken;
7
8pub const INSTALLATIONS_API_URL: &str = "https://firebaseinstallations.googleapis.com/v1";
9const INTERNAL_AUTH_VERSION: &str = "FIS_v2";
10const SDK_VERSION: &str = concat!("w:", env!("CARGO_PKG_VERSION"));
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct RegisteredInstallation {
14    pub fid: String,
15    pub refresh_token: String,
16    pub auth_token: InstallationToken,
17}
18
19#[derive(Serialize)]
20#[serde(rename_all = "camelCase")]
21struct CreateInstallationRequest<'a> {
22    fid: &'a str,
23    auth_version: &'static str,
24    app_id: &'a str,
25    sdk_version: &'static str,
26}
27
28#[derive(Deserialize)]
29#[serde(rename_all = "camelCase")]
30struct CreateInstallationResponse {
31    refresh_token: String,
32    auth_token: GenerateAuthTokenResponse,
33    fid: Option<String>,
34}
35
36#[derive(Serialize)]
37struct GenerateAuthTokenRequest<'a> {
38    installation: GenerateAuthTokenInstallation<'a>,
39}
40
41#[derive(Serialize)]
42#[serde(rename_all = "camelCase")]
43struct GenerateAuthTokenInstallation<'a> {
44    app_id: &'a str,
45    sdk_version: &'static str,
46}
47
48#[derive(Deserialize)]
49#[serde(rename_all = "camelCase")]
50struct GenerateAuthTokenResponse {
51    token: String,
52    expires_in: String,
53}
54
55#[derive(Deserialize)]
56struct ErrorResponse {
57    error: ErrorBody,
58}
59
60#[derive(Deserialize)]
61struct ErrorBody {
62    code: i64,
63    message: String,
64    status: String,
65}
66
67fn convert_auth_token(
68    response: GenerateAuthTokenResponse,
69) -> InstallationsResult<InstallationToken> {
70    let expires_at = SystemTime::now() + parse_expires_in(&response.expires_in)?;
71    Ok(InstallationToken {
72        token: response.token,
73        expires_at,
74    })
75}
76
77fn parse_expires_in(raw: &str) -> InstallationsResult<Duration> {
78    let stripped = raw
79        .strip_suffix('s')
80        .ok_or_else(|| invalid_argument(format!("Invalid expiresIn format: {}", raw)))?;
81    let seconds: u64 = stripped
82        .parse()
83        .map_err(|err| invalid_argument(format!("Invalid expiresIn value '{}': {}", raw, err)))?;
84    Ok(Duration::from_secs(seconds))
85}
86
87#[cfg(not(target_arch = "wasm32"))]
88pub mod native;
89#[cfg(not(target_arch = "wasm32"))]
90pub use native::RestClient;
91
92#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
93pub mod wasm;
94#[cfg(all(target_arch = "wasm32", feature = "wasm-web"))]
95pub use wasm::RestClient;
96
97#[cfg(all(target_arch = "wasm32", not(feature = "wasm-web")))]
98compile_error!(
99    "Building firebase-rs-sdk for wasm32 requires enabling the `wasm-web` feature to include the installations REST client."
100);
101
102#[cfg(test)]
103mod tests;