firebase_admin/auth/
mode.rs1use crate::auth::identity_toolkit::IdentityToolkitEndpoints;
10
11pub const EMULATOR_HOST_ENV_VAR: &str = "FIREBASE_AUTH_EMULATOR_HOST";
14
15#[derive(Debug, Clone)]
18pub enum ClientMode {
19 Live,
21 Emulator {
23 host: String,
25 },
26}
27
28impl ClientMode {
29 pub fn resolve(explicit_emulator_host: Option<String>) -> Self {
33 if let Some(host) = explicit_emulator_host {
34 return ClientMode::Emulator { host };
35 }
36 if let Ok(host) = std::env::var(EMULATOR_HOST_ENV_VAR) {
37 if !host.trim().is_empty() {
38 return ClientMode::Emulator { host };
39 }
40 }
41 ClientMode::Live
42 }
43
44 pub fn endpoints(&self) -> IdentityToolkitEndpoints {
46 match self {
47 ClientMode::Live => IdentityToolkitEndpoints::live(),
48 ClientMode::Emulator { host } => IdentityToolkitEndpoints::emulator(host),
49 }
50 }
51
52 pub fn requires_bearer_token(&self) -> bool {
56 matches!(self, ClientMode::Live)
57 }
58
59 pub fn emulator_api_key(&self) -> Option<&'static str> {
70 match self {
71 ClientMode::Live => None,
72 ClientMode::Emulator { .. } => Some("fake-api-key"),
73 }
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use std::sync::Mutex;
81
82 static ENV_VAR_TEST_LOCK: Mutex<()> = Mutex::new(());
86
87 fn with_emulator_env_var<T>(value: Option<&str>, f: impl FnOnce() -> T) -> T {
88 let _guard = ENV_VAR_TEST_LOCK.lock().unwrap();
89 let previous = std::env::var(EMULATOR_HOST_ENV_VAR).ok();
90 match value {
91 Some(v) => std::env::set_var(EMULATOR_HOST_ENV_VAR, v),
92 None => std::env::remove_var(EMULATOR_HOST_ENV_VAR),
93 }
94
95 let result = f();
96
97 match previous {
98 Some(v) => std::env::set_var(EMULATOR_HOST_ENV_VAR, v),
99 None => std::env::remove_var(EMULATOR_HOST_ENV_VAR),
100 }
101 result
102 }
103
104 #[test]
105 fn explicit_host_wins_over_env_var() {
106 with_emulator_env_var(Some("env-host:9099"), || {
107 let mode = ClientMode::resolve(Some("explicit-host:9099".to_string()));
108 assert!(matches!(mode, ClientMode::Emulator { host } if host == "explicit-host:9099"));
109 });
110 }
111
112 #[test]
113 fn env_var_is_used_when_no_explicit_host_given() {
114 with_emulator_env_var(Some("env-host:9099"), || {
115 let mode = ClientMode::resolve(None);
116 assert!(matches!(mode, ClientMode::Emulator { host } if host == "env-host:9099"));
117 });
118 }
119
120 #[test]
121 fn whitespace_only_env_var_is_treated_as_unset() {
122 with_emulator_env_var(Some(" "), || {
123 let mode = ClientMode::resolve(None);
124 assert!(matches!(mode, ClientMode::Live));
125 });
126 }
127
128 #[test]
129 fn defaults_to_live_with_nothing_set() {
130 with_emulator_env_var(None, || {
131 let mode = ClientMode::resolve(None);
132 assert!(matches!(mode, ClientMode::Live));
133 });
134 }
135
136 #[test]
137 fn requires_bearer_token_only_in_live_mode() {
138 assert!(ClientMode::Live.requires_bearer_token());
139 assert!(!ClientMode::Emulator {
140 host: "localhost:9099".to_string()
141 }
142 .requires_bearer_token());
143 }
144}