Integrate google-cloud with firebase functions - firebase

When integrating google cloud on node.js, as described here:
https://www.npmjs.com/package/google-cloud
One should include a json file with the secret credentials and keys:
var config = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
When using firebase functions / google cloud functions, how can it be done?

Cloud Functions run as a trusted cloud account already. In most cases you shouldn't need to add a credentials JSON file. For example, this is how we initialize Cloud Storage in one of our apps:
const gcs = require('#google-cloud/storage')();
As you see, we're not passing in any authorization information, yet the code is still able to access Cloud Storage:
const file = gcs.bucket('YOUR_FIREBASE_PROJECT_ID.appspot.com').file(filePath);
If you have a case where you need the credentials file:
Download the JSON file with the credentials.
Put the JSON file into your `functions directory.
Refer to it from your code: keyFilename: './keyfile.json'

Related

Firebase Emulator request to Google Cloud Speech to Text Api denied

I would like to test the Google Cloud Speech-to-Text API from within Firebase Emulators. I currently have a trigger set on Firebase Storage that automatically gets fired when I upload a file via the Emulator Storage UI. This makes a request to the Speech to Text API, but I keep getting a permission denied error, as follows:
Error: 7 PERMISSION_DENIED: Cloud Speech-to-Text API has not been used in project 563584335869 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/speech.googleapis.com/overview?project=563584335869 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
I understand that project 563584335869 is the Firebase Cli project.
I have set the following environment variables when starting the emulator:
export GCLOUD_PROJECT=my-actual-glcloud-project-id && export FIREBASE_AUTH_EMULATOR_HOST='localhost:9099' && export GOOGLE_APPLICATION_CREDENTIALS=./path/to/service-account.json &&
firebase emulators:start
The service_account.json key file is associated with a service_account that has the following roles, as demonstrated by running
gcloud projects get-iam-policy my_project_id --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:serviceAccount:my_service_account#my_project_id.iam.gserviceaccount.com"
ROLE
roles/speech.admin
roles/storage.admin
roles/storage.objectAdmin
roles/storage.objectCreator
roles/storage.objectViewer
Since the credentials for the service account I am using should have admin access to the speech to text api, why do I keep getting a permission denied error when running from the emulator, and how can I fix it?
The project id 563584335869 is not yours. It is firebase-cli cloud project’s project-id. In this case, the problem is arising because you have to set your own configuration using your credentials or your key.
You can see below a code for NodeJS which I found in github[1] where it shows how to configure your authentication to use the API.
// Imports the Google Cloud client library
const textToSpeech = require('#google-cloud/text-to-speech');
// Create the auth config
const config = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
// Creates a client
const client = new textToSpeech.TextToSpeechClient(config);
[1]https://github.com/googleapis/nodejs-text-to-speech/issues/26
EDIT
There are different ways to set up your authentication for speech to text. One way to resolve this problem would be to add the same auth configuration as the Text-to-Speech and it should look something like this in your code.
// Imports the Google Cloud client library
const speech = require('#google-cloud/speech');
// Create the auth config
const authconfig = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
// Creates a client
const client = new speech.SpeechClient(authconfig);
Another way to solve this problem according to this Google Cloud Documentation[2] is to setup your authentication.
[2]https://cloud.google.com/speech-to-text/docs/libraries#setting_up_authentication

How to use Firebase Firestore emulator without Firebase Admin

Currently I'm building a nodejs express REST api using the below to access Firestore from my Google Cloud Platform:
const Firestore = require("#google-cloud/firestore");
const db = new Firestore();
const docRef = db.collection('users').doc('alovelace');
const ada = await docRef.set({
first: 'Ada1',
last: 'Lovelace',
born: 1815
});
It picks up the keys from GOOGLE_APPLICATION_CREDENTIALS in a .env file.
Of course I want to develop locally rather than remotely constantly and I've come across the Firebase Firestore Emulator.
I've attempted to use db.useEmulator() but that isn't part of google cloud/firestore but is part of firebase-admin.
Is there any way of using the firestore emulator without going down the firebase-admin route?
The Node.js SDK for client-side development also has a useEmulator call, so you should be able to connect it to the Firestore emulator in the same way.

'auth/invalid-api-key' When Letting the Admin SDK discover a service account

In different pages of the Firebase Admin SDK documentation, e.g., this page, it is suggested that:
If your code is deployed in an environment managed by Google, the
Admin SDK can attempt to auto-discover... the service account
provisioned for your app...To make use of these signing methods,
initialize the SDK with Google Application Default credentials and do
not specify a service account ID string: admin.initializeApp();
When I do this, I get the following error message:
[Error: Your API key is invalid, please check you have copied it
correctly.] code: 'auth/invalid-api-key', message: 'Your API key
is invalid, please check you have copied it correctly.'
Note that I do not get this error message when I manually download and import the credentials and service account JSON files in my project.
Detailed information for reproduction of the error:
1- I'm deploying this on Cloud Functions using Firebase CLI. So, basically, I use firebase deploy.
2- Here is the minimal code in my Node.js App:
const admin = require("firebase-admin");
const config = require("./firebase-config");
admin.initializeApp();
const firebase = require("firebase");
firebase.initializeApp(config);
const functions = require("firebase-functions");
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
const cors = require("cors");
app.use(cors());
app.get("/", Some_Function);
exports.api = functions.https.onRequest(app);
The error happens when I replace firebase.initializeApp(config); with firebase.initializeApp();
You're trying to initialize the Firebase Client SDK in a server-side environment:
const firebase = require("firebase");
firebase.initializeApp(config);
The client SDK accepts a whole different set of credentials compared to firebase-admin. You can initialize firebase-admin without any arguments in GCP managed environments (e.g. Cloud Functions, Cloud Run), but the same doesn't apply to firebase. You need to provide a valid client app configuration obtained from your Firebase project.
const admin = require('firebase-admin');
admin.initializeApp(); // This is ok
const firebase = require('firebase');
firebase.initializeApp(); // This is wrong
See https://firebase.google.com/docs/web/setup#config-object for details on how to obtain a client app configuration.
Also please note that using the client SDK in an environment like Functions is rather unusual. I'd advise you rethink your use case, and see if you really need to use firebase client SDK in your function.
I have tried
dotenv did not work
process.env.PRIVATE_KEY.replace(/\\n/g, '\n') did not work
Step1: go to the console and generate new service account file
Step2: export GOOGLE_APPLICATION_CREDENTIALS='path/to/serviceAccount.json'
(it doesn't matter where it is, e.g : 'user/username/download/serviceAccount.json')
Step3: if(!admin.apps.length) admin.initializeApp(); In the docs, either export the json object as param or no param with step 2

Is firebase sevices.json needed for admin SDK on cloud functions

I am trying to write a function that writes to a different database when a user writes to the default database ,
I did my research and i am a little bit confused
I saw this on firebase
var admin = require('firebase-admin');
var serviceAccount =
require('path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL:
'https://<DATABASE_NAME>.firebaseio.com'
});
And also this
const app1 = firebase.initializeApp({
databaseURL: "https://testapp-1234-1.firebaseio.com"
});
const app2 = firebase.initializeApp({
databaseURL: "https://testapp-1234-2.firebaseio.com"
}, 'app2');
// Get the default database instance for an app1
var database1 = firebase.database();
// Get a database instance for app2
var database1 = firebase.database(app2);
So my question is do we need the service.json file which holds the secret key when using cloud functions admin SDK
For almost all typical use cases, you don't need to provide a service account when working with the Firebase Admin SDK in Cloud Functions. This is because Cloud Functions provides the credentials for the default service account in the project where the function is deployed. (This account can be seen as the App Engine default service account in the Cloud console.)
It's strongly recommended to initialize the Admin SDK with no arguments and accept this default account:
const admin = require('firebase-admin');
admin.initializeApp(); // accept the default service account
As part of taking this default, the Admin SDK will also understand the URL of your default Realtime Database shard, and the URL of your default Cloud Storage bucket. You won't need to provide these values. As such, your code will be much more easy to port to other projects.
If you need to access non-default resources of your project, you can also initialize other instances of firebase-admin to point to those that you explicitly configure. This is what your second code sample is illustrating. If you don't have any non-default resources to access, then don't bother with this.
If there is something that the service account can do, you can grant that role in the Cloud console. The one notable action that this account currently can't perform by default is cryptographic signing of blobs. In practical terms, this means it can't generate signed URLs for object stored in Cloud Storage. This is a fairly common thing to do in Cloud Functions. To remedy this, you need to grant the signBlob role to the default service account.

Reference path for service account key

I am referring to the next article what is "serviceAccountKey.json" referring to in the Firebase device-to-device notifications tutorial
There is one statement written in the index.js file
var serviceAccount = require("path/to/serviceAccountKey.json");
I would like to know what would be the path here when writing the script in the google cloud function web interface.
Do I have to upload the json file on a bucket and then give the bucket reference path?
You should put your serviceAccountKey.json in the same folder as your index.json. Then when you deploy to Cloud Functions, both the code and the key file will be uploaded and you can refer to the JSON from the code with:
var serviceAccount = require("./serviceAccountKey.json");
Note that you may want to consider using the Admin SDK for Firebase Cloud Messaging to send messages these days. This SDK didn't exist when I wrote blog post, but makes the code for sending messages simpler (at the cost of using an extra SDK).

Resources