rs_firebase_admin_sdk/credentials/
mod.rs1pub mod emulator;
4
5use error_stack::{Report, ResultExt};
6use google_cloud_auth::credentials::{CacheableResource, Credentials};
7use headers::HeaderMapExt;
8use headers::{Header, HeaderName, HeaderValue};
9use http::{Extensions, HeaderMap};
10
11#[derive(thiserror::Error, Debug, Clone)]
12#[error("Failed to extract GCP credentials")]
13pub struct GCPCredentialsError;
14
15static X_GOOG_USER_PROJECT: HeaderName = HeaderName::from_static("x-goog-user-project");
16
17pub struct GoogleUserProject(String);
18
19impl Header for GoogleUserProject {
20 fn name() -> &'static HeaderName {
21 &X_GOOG_USER_PROJECT
22 }
23
24 fn decode<'i, I>(values: &mut I) -> Result<Self, headers::Error>
25 where
26 I: Iterator<Item = &'i HeaderValue>,
27 {
28 let value = values
29 .next()
30 .ok_or_else(headers::Error::invalid)?
31 .as_bytes();
32
33 match std::str::from_utf8(value) {
34 Ok(v) => Ok(Self(v.into())),
35 Err(_) => Err(headers::Error::invalid()),
36 }
37 }
38
39 fn encode<E>(&self, values: &mut E)
40 where
41 E: Extend<HeaderValue>,
42 {
43 let value = HeaderValue::from_str(&self.0).unwrap_or_else(|_| HeaderValue::from_static(""));
44
45 values.extend(std::iter::once(value));
46 }
47}
48
49pub(crate) async fn get_project_id(
50 creds: &Credentials,
51) -> Result<String, Report<GCPCredentialsError>> {
52 let headers = get_headers(creds).await?;
53
54 let user_project: GoogleUserProject = headers
55 .typed_get()
56 .ok_or(Report::new(GCPCredentialsError))?;
57
58 Ok(user_project.0)
59}
60
61pub(crate) async fn get_headers(
62 creds: &Credentials,
63) -> Result<HeaderMap, Report<GCPCredentialsError>> {
64 let headers = creds
65 .headers(Extensions::new())
66 .await
67 .change_context(GCPCredentialsError)?;
68
69 let headers = match headers {
70 CacheableResource::New {
71 entity_tag: _,
72 data,
73 } => data,
74 _ => unreachable!(),
75 };
76
77 Ok(headers)
78}