Skip to content
This repository was archived by the owner on Jun 3, 2026. It is now read-only.

Firebase Setup

Aarsh edited this page May 24, 2026 · 1 revision

Firebase Setup

The app uses three Firebase products: Authentication (Google Sign-In via Credential Manager), Firestore (subscriptions & payments), and Analytics (flow audit). All against project helloworld-92567418 (display name Varisankya).


The project

Project ID helloworld-92567418
Project number 663138385072
Android app ID 1:663138385072:android:dfcdd7f25c0171c56e0f75
Package com.hora.varisankya
Web OAuth Client (for Credential Manager) 663138385072-bke7f5oflsl2cg0e5maks0ef3n6o113u.apps.googleusercontent.com
GA4 Property ID 538794512 (linked May 2026)
GA4 Stream ID 14938433408

The Web Client ID is currently hardcoded as WEB_CLIENT_ID in MainActivity.kt. The Google Services plugin also generates R.string.default_web_client_id from google-services.json; consider switching to that to avoid duplication (see Known Issues).


google-services.json

The file lives at app/google-services.json and is gitignored. The canonical copy lives in Bitwarden, split into two base64 chunks (google-services.json [Part 1] + [Part 2]); retrieve_secrets.sh reassembles them.

About the "API key" inside: Firebase Android API keys are identifiers, not secrets. Google explicitly documents that committing google-services.json is fine — access is gated by Android package name + SHA-1 fingerprint + Firestore rules, not by the key. We keep it out of git anyway to follow Play Console hygiene and to keep the keystore + Play service account next to it (those genuinely are secrets) without needing a partial gitignore policy.

If you ever change Firebase project state in the Console (add a new SHA-1, link analytics, etc.), the in-repo file goes stale. Re-pull via:

./retrieve_secrets.sh

…or, for a manual fetch via the API (no Bitwarden involved):

# requires firebase CLI logged in (token at ~/.config/configstore/firebase-tools.json)
python -c "
import json, base64, urllib.request
d = json.load(open('<HOME>/.config/configstore/firebase-tools.json'))
tok = d['tokens']['access_token']
app_id = '1:663138385072:android:dfcdd7f25c0171c56e0f75'
url = f'https://firebase.googleapis.com/v1beta1/projects/helloworld-92567418/androidApps/{app_id}/config'
req = urllib.request.Request(url, headers={'Authorization': 'Bearer ' + tok})
resp = json.load(urllib.request.urlopen(req))
print(base64.b64decode(resp['configFileContents']).decode())
"

Then re-encode and update the Bitwarden item — both halves — so the next ./retrieve_secrets.sh produces the same file.


Authentication (Google Sign-In)

Implemented via the modern Credential Manager API + GoogleIdTokenCredential — not the deprecated GoogleSignInClient. The flow in MainActivity.signInWithGoogle():

  1. Build GetGoogleIdOption with the project's Web OAuth Client ID and setFilterByAuthorizedAccounts(false).
  2. credentialManager.getCredential(activity, request) — Credential Manager shows the system picker.
  3. Extract the ID token from the returned GoogleIdTokenCredential.
  4. FirebaseAuth.signInWithCredential(GoogleAuthProvider.getCredential(idToken, null)).
  5. On success, set up notifications, load subscriptions, observe ViewModel.

Sign-out: auth.signOut() and credentialManager.clearCredentialState(ClearCredentialStateRequest()) (the latter clears the cached account so the user can re-pick on next sign-in).

Required SHA-1 fingerprints

The Firebase Android app must know the SHA-1 of every signing key used to build APKs that try to sign in:

Keystore SHA-1 Notes
Original debug keystore 2b2a3169ef99488ef78ecb66a2f3aee5101bd92a Windows dev machine
Linux debug keystore 4e2eb5c5911b5a2de2a696f78ee3f081d9243b8f Headless Ubuntu CLI
(additional debug keystore) 1c9de1700cd98ef4dba8bdfff54e05ff04931f9d Auto-added Apr 2026
Upload keystore 5cb5895b7729a1d10b90cf0122727d2ef345b6f9 Play upload key
Play App Signing d0fb3d47fa0aade1b062332864ab6d3837e1a00c Cert Play uses to re-sign

To add a new fingerprint:

./gradlew :app:signingReport       # grab SHA1 for the variant you care about
# Firebase Console → Project settings → Your apps → (Android) → Add fingerprint → paste
./retrieve_secrets.sh              # refresh google-services.json + update Bitwarden afterwards

Firestore rules

The deployed rules live in firestore.rules and the Firebase project config in firebase.json + .firebaserc.

Current rules (verbatim):

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {

    // A signed-in user has full read/write access to everything under their
    // own /users/{userId} subtree.
    match /users/{userId}/{document=**} {
      allow read, write: if request.auth != null
                        && request.auth.uid == userId;
    }

    // Collection-group fallback used by the All-Payments page when the flat
    // collection hasn't been populated yet.
    match /{path=**}/payments/{paymentId} {
      allow read: if request.auth != null
                && resource.data.userId == request.auth.uid;
    }
  }
}

The {document=**} rule covers both the legacy nested users/{uid}/subscriptions/{sid}/payments/{pid} path and the flat users/{uid}/payments/{pid} path introduced by the migration. The collection-group rule is what unlocked the fast path for All Payments (without it, collectionGroup("payments") was denied for all users, silently degrading to the slow fan-out).

To redeploy after editing the file:

firebase deploy --only firestore:rules --project helloworld-92567418

(Requires firebase login once.)


Analytics (GA4)

Linked May 2026. The Firebase Analytics SDK was always wired up (it's firebase-analytics in dependencies, and Analytics.init() is called from VarisankyaApplication.onCreate), but events had nowhere to land until the GA4 property was created and the stream mapping established.

To view events: see Analytics & Audit for the dashboard URL, event taxonomy, and DebugView setup.

To re-verify the link state programmatically:

python -c "
import json, urllib.request
d = json.load(open('<HOME>/.config/configstore/firebase-tools.json'))
tok = d['tokens']['access_token']
url = 'https://firebase.googleapis.com/v1beta1/projects/helloworld-92567418/analyticsDetails'
print(json.dumps(json.load(urllib.request.urlopen(
    urllib.request.Request(url, headers={'Authorization': 'Bearer ' + tok}))), indent=2))
"

A linked project returns a streamMappings array (the Android app's stream ID) and an analyticsProperty with the property+account IDs. An unlinked project returns an empty body.


Common failure modes

Symptom Cause Fix
Sign-in fails with DEVELOPER_ERROR / ApiException: 10 SHA-1 of build keystore not in Firebase ./gradlew :app:signingReport, add fingerprint, refresh google-services.json
NoCredentialException Device has no Google account; or hit the system-picker timeout Add a Google account on the device; try again
idToken null after Credential Manager returns Wrong Web Client ID passed to GetGoogleIdOption Confirm WEB_CLIENT_ID matches the project's Web OAuth client (not Android)
Firestore writes succeed but reads return empty Rules blocked the read; check Logcat for PERMISSION_DENIED Re-deploy firestore.rules
All-Payments page is slow Collection-group rule missing (pre-rules_version=2) or no userId field on payments Confirm rule deployed; confirm new payments carry the userId field (they do — PaymentRepository enforces it)
Analytics events not appearing in console First load after enable can take ~24h; or wrong Firebase project selected in console Use DebugView for immediate confirmation; switch to project Varisankya

See also

Clone this wiki locally