Firebase initializeApp with emulators without production config - firebase

I am able to connect to Firebase firestore emulator from a create-react-app app locally using the instructions here. The example code simply includes a comment like this:
// Firebase previously initialized using firebase.initializeApp().
The API reference for initializeApp describes only one way of using it - by passing production configuration to initializeApp and that's the way I got it to work.
However, I am trying to run the app in a self-contained docker environment for the purpose of continuous integration. I do not want any config related to production in this version of the build. By production, I mean any reference that might count towards firebase billing. Is there a way to call initializeApp without production reference? Or are there other ways to handle CI builds?

After some testing, you should initialize app with any string on apiKey:
initializeApp({
apiKey: 'API KEY', // You can set any string here
projectId: 'PROJECT ID HERE', // Optional, you can use here same from .firebaserc
})

We need to use a demo app for this purpose, as explained in this answer. Once we start the emulators described in that answer, initializeApp can be invoked like so:
initializeApp({
projectId: "demo-test",
})
The projectId used in here need to match the one used when starting up the emulator and it needs to start with demo. This ensures there is no way for the app to connect to production services accidentally.

Related

Way to do a proper integration testing with firebase emulators with all services at same time

I want to test my firebase back-end involving firestore, auth, and functions. There are cloud functions on new user created or deleted and rest like apis all affecting firestore data.
I want to run test like this.
I will start firebase emulator (with no data) in a terminal.
Run test file. Following everything will be in a script, so no part is manual.
I will register new users. Those should trigger firebase functions lets say affecting users collection.
Test if the users collection is affected.
Then do a fake login with any created user say Jake Williams.
Call some rest api. Those api will again change firestore data. For these cloud functions auth received will be of Jake Williams
Test if those firestore data is affected.
Clean emulator data and run other tests.
I have gone through docs, yet these is unclear. Went through github/quickStart-testing, still no help. I see there are tutorial which test firestore rules and some test cloud functions separately.
Can I setup a test project to work like my above requirements?
I find Firebase guide confusing. There they mentioned firebase emulators:exec "./my-test.sh", not sure my-test.sh is. Normally not seen .sh file in node.js testing workflow. After 2-3 days, found the proper solution to my own question.
Here are some things you should know.
firebase emulators:exec
firebase emulators:exec 'npm run test' command run your test files within firebase emulator. So when your test files are using firebase admin import * as admin from "firebase-admin"; I think this firebase admin will be linked to that emulator instance being created. Running admin.auth().createUser(...) in that test file, then user will be created on that emulator. This createUser will also trigger side effects in cloud functions. You just have to wait for 5 sec before testing triggered functions effects.
You need to init admin and firebase function test with your project id
import functionTest from 'firebase-functions-test'
import * as admin from "firebase-admin";
const PROJECT_ID = "your project id"
const test = functionTest({
projectId: PROJECT_ID,
});
admin.initializeApp({
projectId: PROJECT_ID,
})
If you want to clear emulator data and reinit app between each tests
import axios from 'axios'
import * as admin from "firebase-admin";
const PROJECT_ID = "your project id"
const deletingFirebaseDataUrl: string = `http://127.0.0.1:8080/emulator/v1/projects/${PROJECT_ID}/databases/(default)/documents`;
const deletingAuthDataUrl: string = `http://localhost:9099/emulator/v1/projects/${PROJECT_ID}/accounts`;
async function initApps() {
admin.initializeApp({
projectId: PROJECT_ID,
})
}
async function deleteAllDataAndApps() {
admin.apps.forEach(app => app?.delete());
await axios.delete(deletingFirebaseDataUrl) //
await axios.delete(deletingAuthDataUrl)
test.cleanup();
}
beforeEach(initApps)
afterEach(deleteAllDataAndApps)
Going through the docs, I did not find these things or they were not clear to me.

Expo: cannot get getDevicePushTokenAsync due to non-valid Firebase project ID (Android)

I just started implementing push notifications in Expo - following the guides on expo.io. Hence, I created an Android app in my Firebase console and my app.json contains now a reference to the Google service file:
"android": {
"package": "------------",
"googleServicesFile": "./google-services.json",
"useNextNotificationsApi": true,
}
If I test this on a real android device (standalone app) I do not receive a valid token via this function:
token = (await Notifications.getExpoPushTokenAsync()).data;
Instead I get the following error:
Error: Encountered an exception while calling native method: Exception
occured while executing exported method getDevicePushTokenAsync on
module ExpoPushTokenManager: Please set your project ID. A valid
Firebase project ID is required to communicate with Firebase server
APIs: It identifies your project with Google."
I guess it has something to do with the way of my project setup. I also followed the Expo guides to setup firebase (e.g. for phone authentication) and hence created a web app in the Firebase console. In my application I initialize the Firebase web app with my web app configuration like this:
const FIREBASE_CONFIG: IFirebaseOptions = {
apiKey: '--------',
authDomain: --------.firebaseapp.com',
databaseURL: 'https://--------.firebaseio.com',
projectId: '--------',
storageBucket: '--------.appspot.com',
messagingSenderId: '--------',
appId: '--------',
measurementId: '',
trackingId: '',
};
firebase.initializeApp(FIREBASE_CONFIG);
The FIREBASE_CONFIG (for my Firebase Web App) as well as the google-services.json (for my Firebase Android App) have the correct project ID. Do I need to initialize the Firebase Android App somehow?
I am using Expo SDK 40.0.1.
What helped me: I deleted the old Firebase Android App, created a new one, downloaded the google-services.json file and built a new version of my app: It worked.
I cannot come up with an explanation why the first Firebase Android App was faulty because I did not make anything different than I did now.
Hopefully that will help some of you as well.
Hey I got this same issue with my react native project. Actually it was working fine but one day this error popped up so I checked the last working version of my files.
The actual issue was with the version of my google services in the gradle file.
Instead on 3.0.0, I have changed it to "classpath 'com.google.gms:google-services:4.3.8'".

React Native with Expo: is firebase-admin necessary?

I need to fetch my firebase db before the authentication process.
My firebase rules are restricted to authenticated users for both read and write.
So I've installed firebase-admin.
in the App.js
import * as admin from 'firebase-admin';
and after initialised it
const serviceAccount = require("./pathtocredentials.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://myappdbname.firebaseio.com"
});
This is the error that I have:
The package at "node_modules\firebase-admin\lib\firebase-namespace.js"
attempted to import the Node standard library module "fs". It failed
because React Native does not include the Node standard library. Read
more at
https://docs.expo.io/versions/latest/introduction/faq/#can-i-use-nodejs-packages-with-expo
Failed building JavaScript bundle.
So I searched and ..."Since your app is running inside JS VM on either iPhone or Android, the only way to access filesystem is to use react-native-fs"
What I want is just to check in my db if a user exists before let it authenticate. Did I necesserily need firebase-admin and react-native-fs ?
firebase-admin is not meant for web frontend code. It's for backend code running nodejs. You won't be able to effectively use firebase-admin in a React or any browser environment. On top of that, it's not really necessary if you just want to query your database from your frontend.
All you really need to do is read data using the normal Firebase JavaScript API for web. You can tell if a node exists in the database by checking if its DataSnapshot object actually exists.

How can I "admin.initializeApp();" no arguments in local

I am always grateful for your help.
I want to write code admin.initializeApp(); both locally and in production.
When I deploy functions to production with no auguments, it works.
But locally, it requires me to write it like below:
const serviceAccount = require("/home/yhirochick/development/ServiceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://xxxx.firebaseio.com/"
});
In the official documentation it says that configuration is applied automatically when you initialize the Firebase Admin SDK with no arguments
But when I execute the command firebase serve --only functions locally and some calls some requests by postman produce the error below:
[2019-07-22T06:45:26.227Z] #firebase/database: FIREBASE WARNING: Provided
authentication credentials for the app named "[DEFAULT]" are invalid. This
usually indicates your app was not initialized correctly. Make sure the
"credential" property provided to initializeApp() is authorized to access the
specified "databaseURL" and is from the correct project.
I want to know How can I "admin.initializeApp();" no arguments locally.
I have grappled with this also and I don't think the local testing scenario currently is explained very well in the official documentation. But here is a solution:
For your local environment you need to download the firebase projects firebase service account json file (found in firebase console under project settings -> service account) and set an environment variable GOOGLE_APPLICATION_CREDENTIALS to point to the file:
# Linux/MACOS version
export GOOGLE_APPLICATION_CREDENTIALS="[PATH_TO_YOUR_SERVICE_ACCOUNT_FILE]"
Read more here, also on how to do this on Windows
Now you will be able to use admin.initializeApp() (with no arguments) locally.
A possible downside of this approach is that you have to set the environment variable each time you fire up a terminal before you start the firebase emulator, because the variable gets deleted when you end the session.
Automate it...
You could automate the export ... command by bundling it together with the command that fires up the emulator. You could do this by adding an entry to the scripts section of your package.json, e.g.:
"local": "export GOOGLE_APPLICATION_CREDENTIALS='[PATH_TO_YOUR_SERVICE_ACCOUNT_FILE]' && firebase emulators:start --only functions"
Then, in this example, you would only need to type npm run local.
Alternative: provide explicit credentials in local environment only
Look at this example: https://stackoverflow.com/a/47517466/1269280.
It basically use a runtime node environment variable to separate between local and production and then use the explicit way of providing credentials in the local environment only.
This is my preferred way of doing things, as I think it is more portable. It allows me to put the service account file inside my codebase and not deal with its absolute file path.
If you do something like this then remember to to exclude the service account file from your repo! (it contains sensitive info).
Background: difference between production and local service account discovery
The reason that admin.initializeApp() (with no arguments) works out-of-the-box in production is that when you deploy to production, i.e. Firebase Functions, the code ends up in a 'Google managed environment'. In Google managed environments like Cloud Functions, Cloud Run, App Engine.. etc, the admin SDK has access to your applications default service account (the one you downloaded above) and will use that when no credentials are specified.
This is part of Google Clouds Application Default Credentials (ADC) strategy which also applies to firebase functions.
Now, your local environment is not a 'google managed environment' so it doesn't have access to the default service account credentials. To google cloud, your local box is just an external server trying to access your private Firebase ressources. So you need to provide your service account credentials in one of the ways described above.
Before I knew this, I thought that because I was already logged in to firebase via my terminal i.e. firebase login and were able to deploy code to firebase, the firebase emulator would also have the necessary credentials for the firebase admin sdk, but this is not the case.

How to get the firebase functions URL in a firebase hosted webapp?

I am working on a project in which we have pre-existing cloud functions in use with Firebase. We are adding a small React SPA using firebase cloud hosting, and this SPA will interact with some of the existing public cloud functions.
The way we have been doing things so far, we have a dev project, and a production project in Firebase. For cloud functions, this works fine, we have environment specific config set up with firebase functions:config:set for differentiations between prod and dev servers.
The problem comes with the hosted SPA contacting the cloud functions. I've seen a lot of questions on how to access the environment config in the hosted code, eg this one: How to reference Firebase Functions config variables from a Firebase-hosted application? where the answer seems to be to have firebase functions that return the values of the environment variables, but for me this just moves the problem further back on step. I fully understand that having the environment variables accessible to this code would be a massive security problem as the SPA is run in the browser.
The only environment specific config I really need for the hosted SPA is the base address for the cloud functions.
eg if in my cloud functions I have
const functions = require('firebase-functions');
const express = require('express');
const test = express();
test.on('/hello/:target', (req, res) => {
res.send(`Hello ${req.params.target}`);
})
exports.test = functions.https.onRequest(test);
then having deployed, this cloud function is available both at https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net/test/hello/world and https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net/test/hello/world . How would I best get the appropriate root url (https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net or https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net) for the project that the SPA is deployed to?
eg. is there some global I can access in the frontend js code where I could do something like:
const url = `${__FIREBASE_GLOBALS__.cloudFunctions.baseUrl}/test/hello/${input}`;
And have the url be correctly defined based on which project the hosted app is deployed to?
I'm assuming here that you're not using Firebase in any other way in your SPA other than to call Cloud Functions (since you didn't say otherwise).
Read the Firebase web setup docs for Firebase Hosting, especially the section on SDK imports and implicit initialization. When you host a site with Firebase Hosting, there are some special URLs that give you the configurations for that project. There are some special script includes that give you access to Firebase products. In particular, note the relative path URI /__/firebase/init.js will yield JavaScript that initializes the Firebase JavaScript SDK with the default settings for your project. Go ahead and access that in a browser pointing to your web app. You're probably interested in the projectId property of the config.
If you want to get a hold of that value, you can use the Firebase SDK, which would be initialized by the script includes from the first link above. Minimally, you could add:
<script src="/__/firebase/5.8.2/firebase-app.js"></script>
<script src="/__/firebase/init.js"></script>
Then later on (see API docs):
firebase.app().options.projectId
to get the ID of the project where Firebase Hosting is serving the content. You can use that to build the URL to your functions.
It might also be convenient for you to port your HTTP functions to callable functions and invoke them from the web site with the Firebase SDK to invoke kthem. Or not.
I was able to get the region and appId from the environment variables.
eg:
console.log(process.env);
Check your firebase logs
{ ...
ENTRY_POINT: 'server',
X_GOOGLE_FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
FIREBASE_CONFIG: '{"projectId":"pid","databaseURL":"https://pid.firebaseio.com","storageBucket":"pid.appspot.com","locationId":"europe-west"}',
X_GOOGLE_FUNCTION_NAME: 'server',
FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
X_GOOGLE_GCLOUD_PROJECT: 'pid',
FUNCTION_NAME: 'server',
X_GOOGLE_GCP_PROJECT: 'pid',
X_GOOGLE_FUNCTION_REGION: 'us-central1',
FUNCTION_REGION: 'us-central1',
X_GOOGLE_ENTRY_POINT: 'server',
GCLOUD_PROJECT: 'pid',
GCP_PROJECT: 'pid',
... ommited
}
Out of these GCP_PROJECT, GCLOUD_PROJECT, FUNCTION_REGION, FUNCTION_NAME should work. So for eg. process.env.FUNCTION_REGION
Not sure how reliable this will be.

Resources