rs_firebase_admin_sdk/credentials/
mod.rs

1//! OAuth2 credential managers for GCP and Firebase Emulator
2
3pub 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}