Firebase Cloud Function config to access other project db - firebase

I need to create a Cloud Function that will access the Firebase DB that is running in another project. If it was accessing the db in the current project, I could use code such as
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
however, what I want is for functions.config().firebase to return the information (and, most importantly, credentials) for the other project. Is there any easy way to do this?

functions.config().firebase is a reserved namespace and you won't be able to override it. However, you can do cross-project initialization yourself. Here's how I would do it:
First, download a service account for your other project into your functions directory. Name it <project_id>-sa.json. Next, set up some environment config (app.other_project_id is just an example name, not a requirement):
firebase functions:config:set app.other_project_id="<the_project_id>"
Now in your code, you can initialize the Admin SDK like so:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
if (!functions.config().app || !functions.config().app.other_project_id) {
throw new Error('Cannot start app without app.other_project_id config.');
}
const FB_PROJECT_ID = functions.config().app.other_project_id;
const SERVICE_ACCOUNT = require(`./${FB_PROJECT_ID}-sa.json`);
admin.initializeApp({
databaseURL: `https://${FB_PROJECT_ID}.firebaseio.com`,
credential: admin.credential.cert(SERVICE_ACCOUNT)
});
This will have initialized the Admin SDK for the other project.

instead of
if (!functions.config().app || !functions.config().app.project_id) {
You should use
if (!functions.config().app || !functions.config().app.fb_project_id) {
NOTE: the typo in project_id

Related

Google storage permissions error while generating signed url from cloud function

I'm attempting to use a Firebase Cloud Function to create signed download URLs for files stored in a Storage Bucket. Using the snippet below on my local machine, I'm able to access cloud storage and generate these URLs.
/* eslint-disable indent */
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
// eslint-disable-next-line #typescript-eslint/no-var-requires
const serviceAccount = require("./test-serviceAccount.json");
admin.initializeApp();
const storage = admin.storage();
const bucket = storage.bucket();
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
}, "firestore");
export const getFile = functions.https.onRequest(async (request, response) => {
const [files] = await bucket.getFiles();
const fileNames: string[] = [];
files.forEach(async (file) => {
console.log(file.name);
const url = await file.getSignedUrl(
{
version: "v2",
action: "read",
expires: Date.now() + 1000 * 60 * 60 * 24,
}
);
fileNames.push(String(url));
if (files.indexOf(file) === files.length - 1) {
response.send(JSON.stringify(fileNames));
}
});
});
However after deploying to Cloud Functions I get an error when I call the function saying:
Error: could not handle the request
and the following message is logged in the functions console:
Error: The caller does not have permission
at Gaxios._request (/workspace/node_modules/gaxios/build/src/gaxios.js:129:23)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async Compute.requestAsync (/workspace/node_modules/google-auth-library/build/src/auth/oauth2client.js:368:18)
at async GoogleAuth.signBlob (/workspace/node_modules/google-auth-library/build/src/auth/googleauth.js:655:21)
at async sign (/workspace/node_modules/#google-cloud/storage/build/src/signer.js:97:35)
I've tried using and not using a .json service account key and made sure that the service account has permissions (it has Service Account Token Creator, Storage Admin, and Editor roles at the moment).
I also read this issue relating to the python SDK for storage, but it seems to have been resolved. The workaround mentioned in that issue (using a .json service account token) also didn't resolve the permissions errors.
After working with Firebase support - here's what worked for me:
import { initializeApp, applicationDefault } from 'firebase-admin/app';
initializeApp({
credential: applicationDefault(),
projectId: '<FIREBASE_PROJECT_ID>',
});
Specifying the projectId in the init call seems to have resolved the issue.
Signed url means it is signed for (or, accessible to) any particular user for particular span of time, maximum 7 days. If you trying to get it for unauthenticated users, it may show such error(s).
It's better to use getDownloadURL() for unauthenticated users. getSignedUrl() should to used for authenticated users only.

Firebase Admin Module Not Found Can't Resolve '#firebase/app' NextJs

I am working on a project that is using Vercel's NextJS build with Firebase Hosting. I've added Firebase functions which I have working well. In the firebaseFunctions.js file I created an export so that I could share the connection to Firebase with other files. However, when trying to use the export I am getting the following error.
The file structure of the project looks like the following.
The following file is the firebaseFunctions.js file that includes the Firebase app initialization and Firestore initialization. All code in this file works when using functions or trying to connect to Firestore.
const { join } = require('path'); // From NextJS Vercel Base Build
const { default: next } = require('next'); // From NextJS Vercel Base Build
const isDev = process.env.NODE_ENV !== 'production'; // From NextJS Vercel Base Build
const nextjsDistDir = join('src', require('./src/next.config.js').distDir); // From NextJS Vercel Base Build
const admin = require('firebase-admin'); // Firebase Admin SDK for NodeJS.
const functions = require('firebase-functions'); // For NextJS + Firebase Functions + Firebase Hosting.
const serviceAccount = require('./serviceAccountKey.json'); // Service account key for Firebase Admin SDK.
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
});
const db = admin.firestore();
const nextjsServer = next({
dev: isDev,
conf: {
distDir: nextjsDistDir,
},
});
const nextjsHandle = nextjsServer.getRequestHandler();
// Nextjs Cloud Function to allow for Firebase Hosting.
exports.nextjsFunc = functions.https.onRequest((req, res) => {
return nextjsServer.prepare().then(() => nextjsHandle(req, res));
});
exports.getCustomToken = functions.https.onCall(async (data, context) => {
// Do something here.
});
module.exports.admin = admin;
module.exports.db = db;
You can see that I export both admin and db here.
In CreateTimer I require db. However this does not allow me access to db.
const db = require('./../firebase/firebase');
Any help with why this may be would be appreciated.
I've done the following.
Verified that Firebase is installed.
Tested Firebase from firebaseFunctions.js
My issue was that I was trying to use the Firebase Admin SDK with React which is a front side language. Firebase Admin can only be used with backend environment like Nodejs. I created a web project in Firebase and then configured this for use in the app.

why I don't get the URL to access the http cloud function I create?

so I make http trigger function to get all events from my firestore like the image above.
firestore.js
const functions = require('firebase-functions')
const admin = require("firebase-admin")
// initialize database
admin.initializeApp()
const db = admin.firestore();
const settings = {timestampsInSnapshots: true};
db.settings(settings)
const eventRef = db.collection('event')
module.getAllEventsFromFirestore = functions.https.onRequest(async (request,response) => {
try {
const events = await eventRef.get()
response.status(200).send(`number of event is ${event.size}`)
} catch (error) {
response.status(500).send(error)
}
})
and my index.js
const {getAllEventsFromFirestore} = require("./firestore")
after deploying the function, I expect will get the URL to access that http trigger function on my terminal, but I can't find it.
The Firebase CLI will only give you a URL the first time you deploy the function. If you update the function after the first deploy, it won't print the URL. You can get the URL of the function by going to the Firebase console and view your functions there. The URL will be available on the Functions dashboard page.
If you would like to see a change in behavior of the Firebase CLI, file a feature request with Firebase support.

Firebase Storage Admin - Bucket not specified-

I've got an onDelete trigger function to delete all files within a folder, and I am following it as per this post from GDE: https://medium.com/google-developer-experts/automatically-delete-your-firebase-storage-files-from-firestore-with-cloud-functions-for-firebase-36542c39ba0d
I initialise my app
const admin = require('firebase-admin');
admin.initializeApp();
And then have my onDelete function
const functions = require('firebase-functions');
const {deleteFiles} = require('../helpers/storageService');
module.exports = functions.firestore.document("designs/{userId}/designs/{designId}").onDelete((snap, context) => {
console.log("triggered");
context.params;
return deleteFiles(context.params)
});
This all works fine, however its within the deleteFiles function that it fails
exports.deleteFiles = async function(params) {
return admin.storage().bucket().deleteFiles({
prefix: `designs/${params.userId}/designs/${params.designId}"`
})
};
When this part of the function runs, it fails with Bucket name not specified or invalid. Specify a valid bucket name via the storageBucket option when initializing the app.
Does anyone know what is happening here? As far as i'm aware, I don't need to provide credentials to the Admin SDK when initialising because the triggers already have sufficient credentials.

Firebase: Update Firestore Fails If Document Does Not Exist [duplicate]

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

Resources