In my project I have setup a dev and production environment by doing this:
const firebaseConfig =
process.env.FUNCTIONS_EMULATOR === "true"
? {
apiKey: process.env.FIREBASE_DEV_KEY,
authDomain: "myapp-dev.firebaseapp.com",
projectId: "myapp-dev",
storageBucket: "myapp-dev.appspot.com",
messagingSenderId: "...",
appId: "...",
measurementId: "...",
}
: {
apiKey: process.env.FIREBASE_PROD_KEY,
authDomain: "myapp-production.firebaseapp.com",
projectId: "myapp-production",
storageBucket: "myapp-production.appspot.com",
messagingSenderId: "...",
appId: "...",
measurementId: "...",
}
admin.initializeApp(firebaseConfig)
Then firestore points to the right project because it was initialized from admin like above:
const firestore = admin.firestore()
However, I am now trying to listen to storage uploads.
And by default, it is listening to my production app. I need to listen to uploads to my dev project.
The problem arises since the storage listener is coming from functions and not admin (which was initialized with the right config).
How can I "initialize" the storage listener (below) so that it listens to the dev project?
exports.generateThumbnail = functions.storage
.object()
.onFinalize(async (object) => {})
Is there maybe a way to attach a listener to admin.storage()?
I'm not sure but it seems like you are using the Client SDK configuration in the Cloud function. The Admin SDK uses a service account instead of the client config.
The FIREBASE_CONFIG environment variable is included automatically in Cloud Functions for Firebase functions that were deployed via the Firebase CLI.
That being said, you just need to initialize the Admin SDK as shown below:
import * as admin from 'firebase-admin';
admin.initializeApp()
Talking about the environments, I can clearly see that you have two different projects for different environments. In that case you would have to deploy the functions to both the project separately because the Admin SDK which uses service account for the dev project will be listening for changes in the dev project only
Other option would be initializing the Admin SDK twice as mentioned in the documentation.
You can also consider using the same Firebase project for both dev and production but use different buckets for them. That way you can listen to those buckets separately. For listening to a specific bucket, specify it's name:
const devFunction = functions.storage.bucket('dev-bucket').object().onFinalize(async (object) => {
//...
})
const prodFunction = functions.storage.bucket('prod-bucket').object().onFinalize(async (object) => {
//...
})
Related
I'm new to firebase. I've employed it into my vuejs project. How do i save the firebase config parameters into environment variables. Or is there a better way to achieve this.
Use .env.js files in vue
This is actually not really needed as you can also store the config directly in the main.js file. Also, these firebase config data is public and needs not to be protected. Still here is how you do it.
In your prod.env.js file (located for my setup under the folder config) add
'use strict'
module.exports = {
NODE_ENV: '"production"',
FIREBASE_API_KEY: '"APIKEY"',
FIREBASE_AUTH_DOMAIN: '"YOURID.firebaseapp.com"',
FIREBASE_DATABASE_URL: '"YOURURL"',
FIREBASE_PROJECT_ID: '"YOURID"',
FIREBASE_STORAGE_BUCKET: '""',
FIREBASE_MESSAGING_SENDER_ID: '"YOURSENDERID"',
FIREBASE_APP_ID: '"YOURAPPID"',
}
In main.js call the env variables with process.env.VARIABLENAME.
Here is the setup for firebase:
const firebaseConfig = {
apiKey: process.env.FIREBASE_API_KEY,
authDomain: process.env.FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.FIREBASE_DATABASE_URL,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.FIREBASE_APP_ID,
};
you can always create a new file that contains all the configuration settings and gitignore that file hence making it secure.
It depends only on your project structure.
Just import this file wherever it is needed without compromising your security
Firebase as release the functions V1.0 and it means a lot of changes in the way to write the code.
I would like to do the migration, I understood all the changes, except the one about the admin.initializeApp
Firebase says that
firebase-admin is now initialized without any parameters within the
Cloud Functions runtime
But I need to access another project database from my actual project.
Until now I was doing it as follows :
/* Initialize the SecondApp service acccount */
var serviceAccount = require("./service-account.json");
/* Initialize SecondApp with the serviceAccount credentials */
var SecondApp = admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
apiKey: "**************",
authDomain: "****************",
databaseURL: "****************",
projectId: "****************",
storageBucket: "****************",
messagingSenderId: "****************"},"SecondApp");
What will be the new way of doing it ?
This is my very basic Cloud Function:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
const db = admin.firestore()
exports.createdNewAccount = functions.auth.user().onCreate(event => {
return db.collection('users').doc(event.data.uid).update({
"creationDate" : Date.now()
})
})
And I get the error
Error: no entity to update: app
What is wrong with my code?
Most likely, the document for event.data.uid does not exist. The documentation for update() states:
The update will fail if applied to a document that does not exist.
Use set() instead.
I faced a similar error when testing my app locally with the Firebase emulator. My firebase config file looked like:
import firebase from "firebase";
import "firebase/firestore";
const firebaseConfig = {
apiKey: <FIREBASE_API_KEY>,
authDomain: <FIREBASE_AUTH_DOMAIN>,
databaseURL: <FIREBASE_DB_URL>,
projectId: <FIREBASE_PROJECT_ID>,
storageBucket: <FIREBASE_STORAGE_BUCKET>,
messagingSenderId: <FIREBASE_MSG_SENDER_ID>,
appId: <FIREBASE_APP_ID>,
measurementId: <FIREBASE_MEASUREMENT_ID>,
};
// Initialize Firebase
const firebaseApp = firebase.initializeApp(firebaseConfig);
// for local instances, use the emulator to connect the frontend to firestore & functions
if (location.hostname === "localhost") {
firebase.firestore().settings({
host: "localhost:8080",
ssl: false,
});
firebase.functions().useFunctionsEmulator("http://localhost:5001");
}
Turns out my local Firestore database (expected to be running at localhost:8080) wasn't being hit. So my cloud functions were trying to write to a non-existent db. The underlying issue was that I also had my backend initializing to a different database:
// functions/index.js
const admin = require("firebase-admin");
var serviceAccount = require("./serviceAccount.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://other-firebase-project-id.firebaseio.com",
});
The solution (originally adapted from a Fireship tutorial) was to remove this [incorrect] re-initialization of the database altogether:
// functions/index.js
...
admin.initializeApp();
...
After all, according to the docs, we can initialize the Firebase Admin SDK without parameters since the FIREBASE_CONFIG environment variable is included automatically in Cloud Functions for Firebase functions that are deployed via the Firebase CLI.
FWIW, also be sure to set the correct Firebase project on the CLI. Doing so with this allows the Firebase CLI to hook Functions/Firestore to the right project:
firebase use <desired-firebase-project-id>
If you have multiple Firebase projects, you can list them out with: firebase projects:list
I am creating a web app that uses Vue webpack with firebase. I would like to have my firebase credentials automatically change when i use firebase use <some_alias> on the firebase cli. In other projects, this simply meant including the /__/firebase/init.js file of firebase hosting. In this project, I am using the npm firebase library and can load in a specific firebase set of credentials with
import firebase from 'firebase'
var config = {
apiKey: '...',
authDomain: '...',
databaseURL: '...',
projectId: '...',
storageBucket: '...',
messagingSenderId: '...'
}
firebase.initializeApp(config)
export default {
database: firebase.database,
storage: firebase.storage,
auth: firebase.auth
}
However, this does not get my credentials based on my current firebase workspace. Instead, I would like something like
import firebase from 'firebase'
const fbcli = require('firebase-tools');
export const getFirebaseInstance = () => {
return fbcli.setup.web().then(config => {
firebase.initializeApp(config)
return firebase
});
}
though synchronous. Is there any way to synchronously load in my firebase credentials?
This was solved by checking window.location.host when in the prod environment and having a production config object if the host was our production hostname and reading from the values of a configuration file otherwise.
Try using fs.writeFileSync as described in this example from a firebase blog post about reading credentials:
const fbcli = require('firebase-tools');
const fs = require('fs');
// by default, uses the current project and logged in user
fbcli.setup.web().then(config => {
fs.writeFileSync(
'build/initFirebase.js',
`firebase.initializeApp(${JSON.stringify(config)});`
);
});
If one integrates this config in JS, won't it be a security concern as any one can open the JS file, get access to this details and access my firebase DB?
var config = {
apiKey: "xxxx",
authDomain: "xxx.firebaseapp.com",
databaseURL: "https://xxx.firebaseio.com",
storageBucket: "xxx.appspot.com",
messagingSenderId: "0000"
};
How does one make sure it's secure?
That's just so the client can identify your app. Even the apiKey is more like a reference and less like a secret password so don't worry. You can use this to instantiate many apps inside a single file. (see https://firebase.google.com/docs/web/setup)
// Intialize the "[DEFAULT]" App
var mainApp = firebase.intializeApp({ ... });
// Intialize a "Secondary" App
var secondaryApp = firebase.initializeApp({ ... }, "Secondary");
...
mainApp.database().ref("path/to/data").set(value);
secondaryApp.database().ref("path/to/data").set(anotherValue);
Now, the heart of Firebase security are the Firebase Realtime Database Rules. Learn them here:
https://firebase.google.com/docs/database/security/
The Firebase Realtime Database Rules are expressed in a JSON-like format, so you should be creating some for yourself in no time!