From 1437e71630f1cd133cdcadb499f75678f2f998e2 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Fri, 20 Mar 2026 16:17:13 +1100 Subject: [PATCH 1/7] Issue-26 - Accept Explicit Project Id. - [x] Added a new method live_with_project_id to allow implementers to appropriately select a dedicated project id. - [x] Updated README.md accordingly. --- README.md | 29 +++++++++++++++++++++++++++++ lib/src/lib.rs | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/README.md b/README.md index 1f30089..c02bf93 100644 --- a/README.md +++ b/README.md @@ -34,4 +34,33 @@ let user = auth_admin.get_user( println!("User id: {}", user.uid); ``` +# Example with an explicit project ID +Use `App::live_with_project_id` when your Firebase project differs from the GCP project in +`GOOGLE_CLOUD_PROJECT` — for example when identity management runs in a separate project from +Cloud Storage and other infrastructure services. + +```rust +use rs_firebase_admin_sdk::{ + auth::{FirebaseAuthService, UserIdentifiers}, + client::ApiHttpClient, + App, +}; + +// Supply the Firebase project ID directly instead of reading GOOGLE_CLOUD_PROJECT +let live_app = App::live_with_project_id("my-firebase-project-id").await.unwrap(); + +let auth_admin = live_app.auth(); + +let user = auth_admin.get_user( + UserIdentifiers::builder() + .with_email("me@email.com".into()) + .build() +) +.await +.expect("Error while fetching user") +.expect("User does not exist"); + +println!("User id: {}", user.uid); +``` + For more examples please see https://github.com/expl/rs-firebase-admin-sdk/tree/main/examples \ No newline at end of file diff --git a/lib/src/lib.rs b/lib/src/lib.rs index c30108f..5f6b797 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -52,6 +52,24 @@ impl App { } impl App { + /// Create instance of Firebase app for live project with an explicit project ID, + /// bypassing environment variable and credential header resolution. + pub async fn live_with_project_id( + project_id: &str, + ) -> Result> { + let credentials: Credentials = Builder::default() + .with_scopes(FIREBASE_AUTH_SCOPES) + .build_access_token_credentials() + .change_context(GCPCredentialsError)? + .into(); + + Ok(Self { + credentials, + project_id: project_id.to_string(), + _credentials_provider: PhantomData, + }) + } + /// Create instance of Firebase app for live project pub async fn live() -> Result> { let credentials: Credentials = Builder::default() From 6e76afa7f0b127b0e949b827def899ab8419acf2 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Fri, 20 Mar 2026 16:22:39 +1100 Subject: [PATCH 2/7] Issue 26 - Accept Explicit Project Id. - [x] Added an example in the examples/ folder --- examples/explicit_project_id/Cargo.toml | 8 +++++ examples/explicit_project_id/src/main.rs | 43 ++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 examples/explicit_project_id/Cargo.toml create mode 100644 examples/explicit_project_id/src/main.rs diff --git a/examples/explicit_project_id/Cargo.toml b/examples/explicit_project_id/Cargo.toml new file mode 100644 index 0000000..4c1e01a --- /dev/null +++ b/examples/explicit_project_id/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "explicit_project_id" +version = "0.1.0" +edition = "2024" + +[dependencies] +rs-firebase-admin-sdk = { path = "../../lib" } +tokio = { version = "1.49", features = ["macros", "rt-multi-thread"] } diff --git a/examples/explicit_project_id/src/main.rs b/examples/explicit_project_id/src/main.rs new file mode 100644 index 0000000..1110752 --- /dev/null +++ b/examples/explicit_project_id/src/main.rs @@ -0,0 +1,43 @@ +/// Demonstrates App::live_with_project_id for environments where the Firebase project +/// differs from the GCP project set in GOOGLE_CLOUD_PROJECT. +/// +/// Usage: +/// FIREBASE_PROJECT_ID=my-firebase-project cargo run --example explicit_project_id +use rs_firebase_admin_sdk::{ + App, + auth::{FirebaseAuthService, UserList}, + client::ApiHttpClient, +}; + +async fn print_all_users(auth_admin: &A) +where + A: FirebaseAuthService, + C: ApiHttpClient, +{ + let mut user_page: Option = None; + loop { + user_page = auth_admin.list_users(10, user_page).await.unwrap(); + + if let Some(user_page) = &user_page { + for user in &user_page.users { + println!("User: {user:?}"); + } + } else { + break; + } + } +} + +#[tokio::main] +async fn main() { + let project_id = std::env::var("FIREBASE_PROJECT_ID") + .expect("FIREBASE_PROJECT_ID must be set"); + + // Credentials are resolved via Application Default Credentials as usual. + // The project ID is taken from the argument instead of GOOGLE_CLOUD_PROJECT, + // so both env vars can point to different GCP projects simultaneously. + let app = App::live_with_project_id(&project_id).await.unwrap(); + let auth_admin = app.auth(); + + print_all_users(&auth_admin).await; +} From c431e6ccc0973b1aec33bcd014a6de241cac875e Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Sun, 22 Mar 2026 12:46:15 +1100 Subject: [PATCH 3/7] Issue 26 - Added example to Cargo.toml --- Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9acab9b..c2a7e8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,5 +6,6 @@ members = [ "examples/get_users", "examples/verify_token", "examples/clear_emulator", - "examples/cookies" + "examples/cookies", + "examples/explicit_project_id" ] \ No newline at end of file From 55d0d2134f611d1e5d4263e14e7a6fc58f3cfdf2 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Sun, 22 Mar 2026 14:08:10 +1100 Subject: [PATCH 4/7] Fixed clippy issues --- examples/explicit_project_id/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/explicit_project_id/src/main.rs b/examples/explicit_project_id/src/main.rs index 1110752..63dba9c 100644 --- a/examples/explicit_project_id/src/main.rs +++ b/examples/explicit_project_id/src/main.rs @@ -30,8 +30,7 @@ where #[tokio::main] async fn main() { - let project_id = std::env::var("FIREBASE_PROJECT_ID") - .expect("FIREBASE_PROJECT_ID must be set"); + let project_id = std::env::var("FIREBASE_PROJECT_ID").expect("FIREBASE_PROJECT_ID must be set"); // Credentials are resolved via Application Default Credentials as usual. // The project ID is taken from the argument instead of GOOGLE_CLOUD_PROJECT, From 09ca5522f3691c5214c6d53b55f2102c1dd4c6b9 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Tue, 24 Mar 2026 22:55:05 +1100 Subject: [PATCH 5/7] Issue-26 - Accept Explicit Project ID. - [x] Removed explicit project id example as per PR comment in https://github.com/expl/rs-firebase-admin-sdk/pull/27\#pullrequestreview-3997213998 --- Cargo.toml | 3 +- examples/explicit_project_id/Cargo.toml | 8 ----- examples/explicit_project_id/src/main.rs | 42 ------------------------ 3 files changed, 1 insertion(+), 52 deletions(-) delete mode 100644 examples/explicit_project_id/Cargo.toml delete mode 100644 examples/explicit_project_id/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index c2a7e8c..9acab9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,5 @@ members = [ "examples/get_users", "examples/verify_token", "examples/clear_emulator", - "examples/cookies", - "examples/explicit_project_id" + "examples/cookies" ] \ No newline at end of file diff --git a/examples/explicit_project_id/Cargo.toml b/examples/explicit_project_id/Cargo.toml deleted file mode 100644 index 4c1e01a..0000000 --- a/examples/explicit_project_id/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "explicit_project_id" -version = "0.1.0" -edition = "2024" - -[dependencies] -rs-firebase-admin-sdk = { path = "../../lib" } -tokio = { version = "1.49", features = ["macros", "rt-multi-thread"] } diff --git a/examples/explicit_project_id/src/main.rs b/examples/explicit_project_id/src/main.rs deleted file mode 100644 index 63dba9c..0000000 --- a/examples/explicit_project_id/src/main.rs +++ /dev/null @@ -1,42 +0,0 @@ -/// Demonstrates App::live_with_project_id for environments where the Firebase project -/// differs from the GCP project set in GOOGLE_CLOUD_PROJECT. -/// -/// Usage: -/// FIREBASE_PROJECT_ID=my-firebase-project cargo run --example explicit_project_id -use rs_firebase_admin_sdk::{ - App, - auth::{FirebaseAuthService, UserList}, - client::ApiHttpClient, -}; - -async fn print_all_users(auth_admin: &A) -where - A: FirebaseAuthService, - C: ApiHttpClient, -{ - let mut user_page: Option = None; - loop { - user_page = auth_admin.list_users(10, user_page).await.unwrap(); - - if let Some(user_page) = &user_page { - for user in &user_page.users { - println!("User: {user:?}"); - } - } else { - break; - } - } -} - -#[tokio::main] -async fn main() { - let project_id = std::env::var("FIREBASE_PROJECT_ID").expect("FIREBASE_PROJECT_ID must be set"); - - // Credentials are resolved via Application Default Credentials as usual. - // The project ID is taken from the argument instead of GOOGLE_CLOUD_PROJECT, - // so both env vars can point to different GCP projects simultaneously. - let app = App::live_with_project_id(&project_id).await.unwrap(); - let auth_admin = app.auth(); - - print_all_users(&auth_admin).await; -} From 607ae380d37a1834359249007bab77156aacb894 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Tue, 24 Mar 2026 23:07:18 +1100 Subject: [PATCH 6/7] Issue-26 - Accept Explicit Project ID. - [x] Updated id_token_verifier() and cookie_token_verifier() to use self.project_id instead of asynchronously getting the project id from credentials. This addresses PR comment https://github.com/expl/rs-firebase-admin-sdk/pull/27\#pullrequestreview-3997213998 --- examples/cookies/src/main.rs | 2 +- examples/verify_token/src/main.rs | 2 +- lib/src/lib.rs | 12 ++++-------- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index 5e30a52..560a868 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -11,7 +11,7 @@ async fn main() { .await .unwrap(); - let live_cookie_validator = live_app.cookie_token_verifier().await.unwrap(); + let live_cookie_validator = live_app.cookie_token_verifier().unwrap(); live_cookie_validator.validate(&cookie).await.unwrap(); } diff --git a/examples/verify_token/src/main.rs b/examples/verify_token/src/main.rs index 1045a7c..ba19002 100644 --- a/examples/verify_token/src/main.rs +++ b/examples/verify_token/src/main.rs @@ -17,7 +17,7 @@ async fn main() { // Live let oidc_token = std::env::var("ID_TOKEN").unwrap(); let live_app = App::live().await.unwrap(); - let live_token_validator = live_app.id_token_verifier().await.unwrap(); + let live_token_validator = live_app.id_token_verifier().unwrap(); verify_token(&oidc_token, &live_token_validator).await; // Emulator diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 5f6b797..76afeec 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -98,23 +98,19 @@ impl App { /// Create OIDC token verifier #[cfg(feature = "tokens")] - pub async fn id_token_verifier( + pub fn id_token_verifier( &self, ) -> Result> { - let project_id = credentials::get_project_id(&self.credentials).await?; - - jwt::LiveValidator::new_jwt_validator(project_id) + jwt::LiveValidator::new_jwt_validator(self.project_id.clone()) .change_context(credentials::GCPCredentialsError) } // /// Create cookie token verifier #[cfg(feature = "tokens")] - pub async fn cookie_token_verifier( + pub fn cookie_token_verifier( &self, ) -> Result> { - let project_id = credentials::get_project_id(&self.credentials).await?; - - jwt::LiveValidator::new_cookie_validator(project_id) + jwt::LiveValidator::new_cookie_validator(self.project_id.clone()) .change_context(credentials::GCPCredentialsError) } } From b1d89715289ec71f7e851781868103e05b3b64f0 Mon Sep 17 00:00:00 2001 From: Sydney-o9 Date: Wed, 25 Mar 2026 11:28:38 +1100 Subject: [PATCH 7/7] Issue-26 - Accept Explicit Project ID. - [x] Reverted functions to async --- examples/cookies/src/main.rs | 2 +- examples/verify_token/src/main.rs | 2 +- lib/src/lib.rs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index 560a868..5e30a52 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -11,7 +11,7 @@ async fn main() { .await .unwrap(); - let live_cookie_validator = live_app.cookie_token_verifier().unwrap(); + let live_cookie_validator = live_app.cookie_token_verifier().await.unwrap(); live_cookie_validator.validate(&cookie).await.unwrap(); } diff --git a/examples/verify_token/src/main.rs b/examples/verify_token/src/main.rs index ba19002..1045a7c 100644 --- a/examples/verify_token/src/main.rs +++ b/examples/verify_token/src/main.rs @@ -17,7 +17,7 @@ async fn main() { // Live let oidc_token = std::env::var("ID_TOKEN").unwrap(); let live_app = App::live().await.unwrap(); - let live_token_validator = live_app.id_token_verifier().unwrap(); + let live_token_validator = live_app.id_token_verifier().await.unwrap(); verify_token(&oidc_token, &live_token_validator).await; // Emulator diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 76afeec..130ee2a 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -98,7 +98,7 @@ impl App { /// Create OIDC token verifier #[cfg(feature = "tokens")] - pub fn id_token_verifier( + pub async fn id_token_verifier( &self, ) -> Result> { jwt::LiveValidator::new_jwt_validator(self.project_id.clone()) @@ -107,7 +107,7 @@ impl App { // /// Create cookie token verifier #[cfg(feature = "tokens")] - pub fn cookie_token_verifier( + pub async fn cookie_token_verifier( &self, ) -> Result> { jwt::LiveValidator::new_cookie_validator(self.project_id.clone())