1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
pub mod api_uri;
pub mod auth;
pub mod client;
pub mod credentials;
pub mod util;

use auth::FirebaseAuth;

#[cfg(feature = "tokens")]
use auth::token::{
    cache::{HttpCache, PubKeys},
    error::TokenVerificationError,
    EmulatedTokenVerifier, LiveTokenVerifier, GOOGLE_COOKIE_PUB_KEY_URI, GOOGLE_PUB_KEY_URI,
};
use client::ReqwestApiClient;
use credentials::emulator::EmulatorCredentials;
pub use credentials::{error::CredentialsError, Credentials};
use error_stack::{Report, ResultExt};
pub use gcp_auth::provider as credentials_provider;
use gcp_auth::TokenProvider;
use std::sync::Arc;

/// Default Firebase Auth admin manager
pub type GcpCredentials = Arc<dyn TokenProvider>;
pub type LiveAuthAdmin = FirebaseAuth<ReqwestApiClient<GcpCredentials>>;
/// Default Firebase Auth Emulator admin manager
pub type EmulatorAuthAdmin = FirebaseAuth<ReqwestApiClient<EmulatorCredentials>>;

/// Base privileged manager for Firebase
pub struct App<CredentialsT> {
    credentials: CredentialsT,
    project_id: String,
}

impl App<EmulatorCredentials> {
    /// Firebase app backend by emulator
    pub fn emulated(project_id: String) -> Self {
        Self {
            credentials: EmulatorCredentials {},
            project_id,
        }
    }

    /// Firebase authentication manager for emulator
    pub fn auth(&self, emulator_url: String) -> EmulatorAuthAdmin {
        let client = ReqwestApiClient::new(reqwest::Client::new(), self.credentials.clone());

        FirebaseAuth::emulated(emulator_url, &self.project_id, client)
    }

    /// OIDC token verifier for emulator
    #[cfg(feature = "tokens")]
    pub fn id_token_verifier(&self) -> EmulatedTokenVerifier {
        EmulatedTokenVerifier::new(self.project_id.clone())
    }
}

impl App<GcpCredentials> {
    /// Create instance of Firebase app for live project
    pub async fn live(credentials: GcpCredentials) -> Result<Self, Report<CredentialsError>> {
        Self::live_shared(credentials).await
    }

    pub async fn live_shared(
        credentials: GcpCredentials,
    ) -> Result<Self, Report<CredentialsError>> {
        let project_id = credentials
            .project_id()
            .await
            .change_context(CredentialsError::Internal)?
            .to_string();

        Ok(Self {
            credentials,
            project_id,
        })
    }

    /// Create Firebase authentication manager
    pub fn auth(&self) -> LiveAuthAdmin {
        let client = ReqwestApiClient::new(reqwest::Client::new(), self.credentials.clone());

        FirebaseAuth::live(&self.project_id, client)
    }

    /// Create OIDC token verifier
    #[cfg(feature = "tokens")]
    pub async fn id_token_verifier(
        &self,
    ) -> Result<
        LiveTokenVerifier<HttpCache<reqwest::Client, PubKeys>>,
        Report<TokenVerificationError>,
    > {
        let cache_client = HttpCache::new(
            reqwest::Client::new(),
            GOOGLE_PUB_KEY_URI
                .parse()
                .map_err(error_stack::Report::new)
                .change_context(TokenVerificationError::FailedGettingKeys)?,
        )
        .await
        .change_context(TokenVerificationError::FailedGettingKeys)?;

        LiveTokenVerifier::new_id_verifier(self.project_id.clone(), cache_client)
    }

    /// Create cookie token verifier
    #[cfg(feature = "tokens")]
    pub async fn cookie_token_verifier(
        &self,
    ) -> Result<
        LiveTokenVerifier<HttpCache<reqwest::Client, PubKeys>>,
        Report<TokenVerificationError>,
    > {
        let cache_client = HttpCache::new(
            reqwest::Client::new(),
            GOOGLE_COOKIE_PUB_KEY_URI
                .parse()
                .map_err(error_stack::Report::new)
                .change_context(TokenVerificationError::FailedGettingKeys)?,
        )
        .await
        .change_context(TokenVerificationError::FailedGettingKeys)?;

        LiveTokenVerifier::new_cookie_verifier(self.project_id.clone(), cache_client)
    }
}