{"id":15024602,"url":"https://github.com/davidgraeff/firestore-db-and-auth-rs","last_synced_at":"2025-11-04T21:02:23.954Z","repository":{"id":44937320,"uuid":"189990101","full_name":"davidgraeff/firestore-db-and-auth-rs","owner":"davidgraeff","description":"Easy Rust access to your Google Firestore DB via service account or OAuth impersonated Google Firebase Auth credentials","archived":false,"fork":false,"pushed_at":"2024-01-23T20:11:39.000Z","size":222,"stargazers_count":102,"open_issues_count":9,"forks_count":37,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-10-19T10:54:20.022Z","etag":null,"topics":["authentication","firebase","firestore"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/davidgraeff.png","metadata":{"files":{"readme":"readme.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-03T11:11:53.000Z","updated_at":"2025-05-24T07:08:11.000Z","dependencies_parsed_at":"2024-09-20T08:04:47.926Z","dependency_job_id":"e0b23476-10ee-409e-babf-e9f5cdb3ee90","html_url":"https://github.com/davidgraeff/firestore-db-and-auth-rs","commit_stats":{"total_commits":84,"total_committers":6,"mean_commits":14.0,"dds":0.1785714285714286,"last_synced_commit":"beedaf332ceaff054842f7d30558b7f0ba2f5d5a"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/davidgraeff/firestore-db-and-auth-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Ffirestore-db-and-auth-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Ffirestore-db-and-auth-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Ffirestore-db-and-auth-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Ffirestore-db-and-auth-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/davidgraeff","download_url":"https://codeload.github.com/davidgraeff/firestore-db-and-auth-rs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/davidgraeff%2Ffirestore-db-and-auth-rs/sbom","scorecard":{"id":326710,"data":{"date":"2025-08-11","repo":{"name":"github.com/davidgraeff/firestore-db-and-auth-rs","commit":"0b8cfe5476f396d8f85e2d4aaee4155a6bc27eb8"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":1,"reason":"Found 3/20 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/integration.yml:1","Warn: no topLevel permission defined: .github/workflows/style.yml:1","Warn: no topLevel permission defined: .github/workflows/with_rocket.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: third-party GitHubAction not pinned by hash: .github/workflows/integration.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/davidgraeff/firestore-db-and-auth-rs/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/integration.yml:26: update your workflow using https://app.stepsecurity.io/secureworkflow/davidgraeff/firestore-db-and-auth-rs/integration.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/style.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/davidgraeff/firestore-db-and-auth-rs/style.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/with_rocket.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/davidgraeff/firestore-db-and-auth-rs/with_rocket.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/with_rocket.yml:30: update your workflow using https://app.stepsecurity.io/secureworkflow/davidgraeff/firestore-db-and-auth-rs/with_rocket.yml/master?enable=pin","Info:   0 out of   3 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 19 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-18T02:38:13.354Z","repository_id":44937320,"created_at":"2025-08-18T02:38:13.354Z","updated_at":"2025-08-18T02:38:13.354Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":282713022,"owners_count":26714760,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-04T02:00:05.887Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","firebase","firestore"],"created_at":"2024-09-24T20:00:37.897Z","updated_at":"2025-11-04T21:02:23.908Z","avatar_url":"https://github.com/davidgraeff.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Firestore API and Auth\n\n\u003cimg alt=\"Firestore Logo, Copyright by Google\" align=\"right\" src=\"https://github.com/davidgraeff/firestore-db-and-auth-rs/raw/master/doc/logo.png\" /\u003e\n\n[![Integration](https://github.com/davidgraeff/firestore-db-and-auth-rs/actions/workflows/integration.yml/badge.svg)](https://github.com/davidgraeff/firestore-db-and-auth-rs/actions/workflows/integration.yml)\n[![With Rocket](https://github.com/davidgraeff/firestore-db-and-auth-rs/actions/workflows/with_rocket.yml/badge.svg)](https://github.com/davidgraeff/firestore-db-and-auth-rs/actions/workflows/with_rocket.yml)\n[![](https://meritbadge.herokuapp.com/firestore-db-and-auth)](https://crates.io/crates/firestore-db-and-auth)\n[![](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)\n![docs.rs](https://img.shields.io/docsrs/firestore-db-and-auth)\n\nThis crate allows easy access to your Google Firestore DB via service account or OAuth impersonated Google Firebase Auth credentials.\nMinimum Rust version: 1.64\n\nFeatures:\n* Asynchronous API\n* Subset of the Firestore v1 API\n* Optionally handles authentication and token refreshing for you\n* Support for the downloadable Google service account json file from [Google Clound console](https://console.cloud.google.com/apis/credentials/serviceaccountkey).\n  (See https://cloud.google.com/storage/docs/reference/libraries#client-libraries-install-cpp)\n\nUse-cases:\n* Strictly typed document read/write/query access\n* Cloud functions (Google Compute, AWS Lambda) access to Firestore\n\nLimitations:\n* Listening to document / collection changes is not yet possible\n\n### Cargo features\n\n* **native-tls**, **default-tls**, **rustls-tls**: Choose any of those features for encrypted connections (https).\n  rustls-tls is the default (the rustls crate will be used).\n\n* **rocket_support**: [Rocket](https://rocket.rs/) is a web framework.\n  This feature enables rocket integration and adds a [Request Guard](https://rocket.rs/v0.4/guide/requests/#request-guards).\n  Only Firestore Auth authorized requests can pass this guard.\n\n### Document operations\n\nThis crate operates on DTOs (Data transfer objects) for type-safe operations on your Firestore DB.\n\n```rust,no_run\nuse firestore_db_and_auth::{Credentials, ServiceSession, documents, errors::Result};\nuse serde::{Serialize,Deserialize};\n\n #[derive(Serialize, Deserialize)]\n struct DemoDTO {\n    a_string: String,\n    an_int: u32,\n    another_int: u32,\n }\n #[derive(Serialize, Deserialize)]\n struct DemoPartialDTO {\n    #[serde(skip_serializing_if = \"Option::is_none\")]\n    a_string: Option\u003cString\u003e,\n    an_int: u32,\n }\n\n/// Write the given object with the document id \"service_test\" to the \"tests\" collection.\n/// You do not need to provide a document id (use \"None\" instead) and let Firestore generate one for you.\n/// \n/// In either way a document is created or updated (overwritten).\n/// \n/// The write method will return document metadata (including a possible generated document id)\nfn write(session: \u0026ServiceSession) -\u003e Result\u003c()\u003e {\n    let obj = DemoDTO { a_string: \"abcd\".to_owned(), an_int: 14, another_int: 10 };\n    let result = documents::write(session, \"tests\", Some(\"service_test\"), \u0026obj, documents::WriteOptions::default())?;\n    println!(\"id: {}, created: {}, updated: {}\", result.document_id, result.create_time.unwrap(), result.update_time.unwrap());\n    Ok(())\n}\n\n/// Only write some fields and do not overwrite the entire document.\n/// Either via Option\u003c\u003e or by not having the fields in the structure, see DemoPartialDTO.\nfn write_partial(session: \u0026ServiceSession) -\u003e Result\u003c()\u003e {\n    let obj = DemoPartialDTO { a_string: None, an_int: 16 };\n    let result = documents::write(session, \"tests\", Some(\"service_test\"), \u0026obj, documents::WriteOptions{merge:true})?;\n    println!(\"id: {}, created: {}, updated: {}\", result.document_id, result.create_time.unwrap(), result.update_time.unwrap());\n    Ok(())\n}\n```\n\nRead the document with the id \"service_test\" from the Firestore \"tests\" collection:\n\n```rust,no_run\nuse firestore_db_and_auth::{documents};\nlet obj : DemoDTO = documents::read(\u0026session, \"tests\", \"service_test\")?;\n```\n\nFor listing all documents of the \"tests\" collection you want to use the `list` method which implements an async stream.\nThis hide the complexity of the paging API and fetches new documents when necessary:\n\n```rust,no_run\nuse firestore_db_and_auth::{documents};\n\nlet mut stream = documents::list(\u0026session, \"tests\");\nwhile let Some(Ok(doc_result)) = stream.next().await {\n    // The document is wrapped in a Result\u003c\u003e because fetching new data could have failed\n    let (doc, _metadata) = doc_result;\n    let doc: DemoDTO = doc;\n    println!(\"{:?}\", doc);\n}\n```\n\n*Note:* The resulting list or list cursor is a snapshot view with a limited lifetime.\nYou cannot keep the iterator/stream for long or expect new documents to appear in an ongoing iteration.\n\nFor querying the database you would use the `query` method.\nIn the following example the collection \"tests\" is queried for document(s) with the \"id\" field equal to \"Sam Weiss\".\n\n```rust,no_run\nuse firestore_db_and_auth::{documents, dto};\n\nlet values = documents::query(\u0026session, \"tests\", \"Sam Weiss\".into(), dto::FieldOperator::EQUAL, \"id\").await?;\nfor metadata in values {\n    println!(\"id: {}, created: {}, updated: {}\", metadata.name.as_ref().unwrap(), metadata.create_time.as_ref().unwrap(), metadata.update_time.as_ref().unwrap());\n    // Fetch the actual document\n    // The data is wrapped in a Result\u003c\u003e because fetching new data could have failed\n    let doc : DemoDTO = documents::read_by_name(\u0026session, metadata.name.as_ref().unwrap())?;\n    println!(\"{:?}\", doc);\n}\n```\n\nDid you notice the `into` on `\"Sam Weiss\".into()`?\nFirestore stores document fields strongly typed.\nThe query value can be a string, an integer, a floating point number and potentially even an array or object (not tested).\n\n*Note:* The query method returns a vector, because a query potentially returns multiple matching documents.\n\n### Error handling\n\nThe returned `Result` will have a `FirebaseError` set in any error case.\nThis custom error type wraps all possible errors (IO, Reqwest, JWT errors etc)\nand Google REST API errors. If you want to specifically check for an API error,\nyou could do so:\n\n```rust,no_run\nuse firestore_db_and_auth::{documents, errors::FirebaseError};\n\nlet r = documents::delete(\u0026session, \"tests/non_existing\", true).await;\nif let Err(e) = r.err() {\n    if let FirebaseError::APIError(code, message, context) = e {\n        assert_eq!(code, 404);\n        assert!(message.contains(\"No document to update\"));\n        assert_eq!(context, \"tests/non_existing\");\n    }\n}\n```\n\nThe code is numeric, the message is what the Google server returned as message.\nThe context string depends on the called method.\nIt may be the collection or document id or any other context information.\n\n### Document access via service account\n\n1. Download the service accounts credentials file and store it as \"firebase-service-account.json\".\n   The file should contain `\"private_key_id\": ...`.\n2. Add another field `\"api_key\" : \"YOUR_API_KEY\"` and replace YOUR_API_KEY with your *Web API key*, to be found in the [Google Firebase console](https://console.firebase.google.com) in \"Project Overview -\u003e Settings - \u003e General\".\n\n```rust,no_run\nuse firestore_db_and_auth::{Credentials, ServiceSession};\n\n/// Create credentials object. You may as well do that programmatically.\nlet cred = Credentials::from_file(\"firebase-service-account.json\").await\n    .expect(\"Read credentials file\")\n    .download_jwkset().await\n    .expect(\"Failed to download public keys\");\n/// To use any of the Firestore methods, you need a session first. You either want\n/// an impersonated session bound to a Firebase Auth user or a service account session.\nlet session = ServiceSession::new(\u0026cred)\n    .expect(\"Create a service account session\");\n```\n\n### Document access via a firebase user access / refresh token or via user_id\n\nYou can create a user session in various ways.\nIf you just have the firebase Auth user_id, you would follow these steps:\n\n```rust,no_run\nuse firestore_db_and_auth::{Credentials, sessions};\n\n/// Create credentials object. You may as well do that programmatically.\nlet cred = Credentials::from_file(\"firebase-service-account.json\").await\n    .expect(\"Read credentials file\")\n    .download_jwkset().await\n    .expect(\"Failed to download public keys\");\n\n/// To use any of the Firestore methods, you need a session first.\n/// Create an impersonated session bound to a Firebase Auth user via your service account credentials.\nlet session = UserSession::by_user_id(\u0026cred, \"the_user_id\").await\n    .expect(\"Create a user session\");\n```\n\nIf you already have a valid refresh token and want to generate an access token (and a session object), you do this instead:\n\n```rust,no_run\nlet refresh_token = \"fkjandsfbajsbfd;asbfdaosa.asduabsifdabsda,fd,a,sdbasfadfasfas.dasdasbfadusbflansf\";\nlet session = UserSession::by_refresh_token(\u0026cred, \u0026refresh_token).await?;\n```\n\nAnother way of retrieving a session object is by providing a valid access token like so:\n\n```rust,no_run\nlet access_token = \"fkjandsfbajsbfd;asbfdaosa.asduabsifdabsda,fd,a,sdbasfadfasfas.dasdasbfadusbflansf\";\nlet session = UserSession::by_access_token(\u0026cred, \u0026access_token).await?;\n```\n\nThe `by_access_token` method will fail if the token is not valid anymore.\nPlease note that a session created this way is not able to automatically refresh its access token.\n(There is no *refresh_token* associated with it.)\n\n## Cloud functions: Improve cold-start time\n\nThe usual start up procedure includes three IO operations:\n\n* downloading two public jwks keys from a Google server,\n* and read in the json credentials file.\n\nAvoid those by embedding the credentials and public key files into your application.\n\nFirst download the 2 public key files:\n\n* https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com -\u003e Store as `securetoken.jwks`\n* https://www.googleapis.com/service_accounts/v1/jwk/{your-service-account-email} -\u003e Store as `service-account.jwks`\n* Merge the two files into one `firebase-service-account.jwks` \n\nCreate a `Credentials` object like so:\n\n```rust,no_run\nuse firestore_db_and_auth::Credentials;\nlet c = Credentials::new(include_str!(\"firebase-service-account.json\")).await?\n    .with_jwkset(\u0026JWKSet::new(include_str!(\"firebase-service-account.jwks\"))?).await?;\n```\n\n\u003e Please note though, that Googles JWK keys change periodically.\n\u003e You probably want to redeploy your service with fresh public keys about every three weeks.\n\u003e \n\u003e For long-running service you want to call Credentials::download_google_jwks() periodically.\n\n### More information\n\n* [Firestore Auth: Background information](doc/auth_background.md)\n* [Use your own authentication implementation](doc/own_auth.md)\n* [Http Rocket Server integration](doc/rocket_integration.md)\n* Build the documentation locally with `cargo +nightly doc --features external_doc,rocket_support`\n\n## Testing\n\nTo perform a full integration test (`cargo test`), you need a valid \"firebase-service-account.json\" file.\nThe tests expect a Firebase user with the ID given in `examples/test_user_id.txt` to exist.\n[More Information](/doc/integration_tests.md)\n\n#### What can be done to make this crate more awesome\n\nThis library does not have the ambition to mirror the http/gRPC API 1:1.\nThere are auto-generated libraries for this purpose. But the following fits into the crates schema:\n\n* Nice to have: Transactions, batch_get support for Firestore\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidgraeff%2Ffirestore-db-and-auth-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdavidgraeff%2Ffirestore-db-and-auth-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdavidgraeff%2Ffirestore-db-and-auth-rs/lists"}