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

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.

Related

firebase-admin, is it contradicting to use default credentials and serviceAccountId in same initialization?

I initialize firebase-admin like so for firebase functions:
const admin = require('firebase-admin')
const { applicationDefault } = require('firebase-admin/app')
admin.initializeApp({
credential: applicationDefault(),
serviceAccountId: 'firebase-adminsdk-hash#project-id.iam.gserviceaccount.com',
})
I had to add the serviceAccountId so that I could access another IAM permission. But I'm not sure if it's now unnecessary use applicationDefault() as well. Does anyone know if this is considered redundant / contradicting?
The short answer, in my opinion, would be: yes, it is redundant and inconsistent to specify the applicationDefault parameter as it is implicitly populated from the FIREBASE_CONFIG environment variable as explained in this previous question. (Please be aware the initialization can be different depending on the environment).
Here is a comparison from initializing the service account and the Cloud Functions together and impersonating the service account.
To get a more detailed explanation, we will need to dive into the documentation; we can see that the initializeApp() function has the AppOptions parameter (interface), where you can specify the serviceAccountId.
Finally, you can check all the functions from firebase-admin/app module, and get this description for the applicationDefault(httpAgent) function:
Returns a credential created from the Google Application Default Credentials that grants admin access to Firebase services. This credential can be used in the call to initializeApp().
Google Application Default Credentials are available on any Google infrastructure, such as Google App Engine and Google Compute Engine. See Initialize the SDK for more details.

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

Accessing firestore from self-hosted server

I need to be able to access (read, write) firestore from my self-hosted server (not on gcloud).
I also need to reserve write permissions to the app hosted on my self-hosted server, and allow noone else to be able to write to the server, so the security rules for write applications are denied by default.
According to this document:
https://firebase.google.com/docs/firestore/client/libraries#server_client_libraries
using a service account should allow me to be able to access all firebase products, including firestore, without any restrictions.
But, as soon as I restrict the security rules, I am unable to write to firestore from my service, instantiated with service account credentials following the steps in this documentation:
https://firebase.google.com/docs/admin/setup#initialize-sdk
I get the following error:
PERMISSION_DENIED: Missing or insufficient permissions.
What do I have to do to allow my application to write to firestore from my own servers?
Update:
I am using java for my backend applications.
Servers (or cloud functions) are secure environment which can be accessed only by you and people you have authorized. You don't actually use the Firebase Client SDKs in a server. Instead you use the Firebase Admin SDK. It uses a service account and has privileged access to your Firebase Project.
What does privileged access mean?
The documentation has explained it all but long story short it has complete access like you to the project. It does not follow any security rules.
If you are implementing the Admin SDK which will deal with API requests coming from your clients then make sure you authenticate them. To do so you would ideally pass the Firebase ID Token from your frontend to the API and verify
it using the Admin SDK which returns a decoded token object containing UID, custom claims and some metadata about that user.
I'm not sure which language you use but here's a simple getting started for NodeJS:
// Install the Admin SDK
npm install firebase-admin
// index.js
const admin = require("firebase-admin")
const path = require("path");
admin.initializeApp({
credential: admin.credential.cert("path/to/key")),
databaseURL: "https://<project-id>.firebaseio.com/"
})
const firestore = admin.firestore()
const auth = admin.auth()
// firebase.firestore() and firebase.auth() in client SDKs
Rest of the syntax is mostly the same as Client SDK.
You can create a service account from the project settings.
The issue that I was facing has already been reported here:
https://github.com/googleapis/java-firestore/issues/431
The fix was an odd one, and cannot be explained without going deep into the SDK and how it works, which I did not have time to check:

Firebase.firestore vs admin.firestore

I'm learning GCP and in their Firestore, I'm confused with the difference of Admin.firestore & Firebase.firestore.
this is the code for admin:
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "https://<firestoreprojectnameurl>"
const db = admin.firestore();
});
while this is the code for the firestore
const { config } = require('./config');
const firebase = require("firebase");
firebase.initializeApp(config);
const db = firebase.firestore();
Please note that only 1 db at a time will work and for my current set-up I use the db = firebase.firestore() although if I change it to db = admin.firestore ite works fine and all my code works the same.
Thank you in advance!
The JavaScript SDK for web clients (your second example) is different than the JavaScript SDK for nodejs backends (your first example). They have different APIs, though they might appear very similar for most types of queries. But they are definitely not interchangeable. You are supposed to pick the one that matches the environment where it's going to be used. The Firebase Admin SDK is definitely not usable in web clients, though the web client SDK might work in nodejs backend environments (but I don't recommend it).
It might also help to know that the Firebase Admin SDK is actually just a wrapper around the Google Cloud nodejs SDK. You can compare the API documentation of the web SDK to the nodejs SDK if you want to take a closer look.

How do you best manage database URL in firebase cloud functions?

I have a dev database and a staging database and I want my firebase cloud functions to use whichever database is appropriate based on where it is deployed, is there a variable or something that I can reference for this so that I dont have to manually change the URL before every deploy to dev or staging environment?
var FirebaseDBUrlVar = 'some-url-to-firebase-dev';
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: FirebaseDBUrlVar,
});
Starting version version 1.0 of the firebase-functions SDK, you can initialize the SDK with no arguments, and it will automatically pick up all the defaults for your environment:
admin.initializeApp()
If you need to add a service account to that, you can parse the defaults out of process.env.FIREBASE_CONFIG and add the credential to it:
const serviceAccount = require('./service-account-credentials.json')
const adminConfig = JSON.parse(process.env.FIREBASE_CONFIG)
adminConfig.credential = admin.credential.cert(serviceAccount)
admin.initializeApp(adminConfig)

Resources