Calling `subscribeToTopic` from a locally run cloud function - node.js

I keep getting a permission error...
"An error occurred when trying to authenticate to the FCM servers. Make sure the credential used to authenticate this SDK has the proper permissions. See https://firebase.google.com/docs/admin/setup for setup instructions."
...when I locally run a cloud function that calls to subscribeToTopic and unsubscribeToTopic. This problem des not happen when I deploy my function to firebase.
I am using firebase-admin and set up firebase in my cloud function via...
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
The authentication issue only happened when calling subscribeToTopic and unsubscribeToTopic. Writing to the db from the cloud function works fine.
I was able to get around this issue by calling admin.initializeApp w/ a generated service account json file that I created via the firebase console.
Any ideas as to why this happened?

Related

How to access firebase database from nodejs admin sdk after enforcing Firebase App Check

I have enabled Firebase App Check in my project to ensure all requests are coming only from our app. That has been working well till now (Using RecaptachaV3 as a Attestation Provider for web). However I want to access the database (rtdb) from my backend nodejs server. I'm using the firebase-admin-sdk with a service account for it. But I am unable to do it.
const { initializeApp, applicationDefault } = require('firebase-admin/app');
const { getDatabase } = require('firebase-admin/database');
const app = initializeApp({
credential: applicationDefault(),
databaseURL: '********',
});
const db = getDatabase(app);
const ref = db.ref('/some-path');
ref.once('value', (snapshot) => {
console.log(snapshot.val());
});
Below error is thrown
[2022-06-27T09:42:25.299Z] #firebase/database: FIREBASE WARNING: Missing appcheck token (https://qtalkproject.firebaseio.com/)
Isn't app check only for clients? Shouldn't firebase allow all requests that are coming from a service account? Why is it asking for an app check token? Acc to the documentation the firebase-admin-sdk is allowed to create app check tokens. If it is allowed to create app check tokens, doesn't it mean it is already authenticated? Why can't I access the database from the same admin sdk then? Am I missing something here?
If anyone else is blocked like me until firebase releases the fix, you can use the older version of the firebase admin sdk which doesn't have app check (I used v9.3.0) to access rtdb and other firebase features
Firebaser here
You are correct, the Firebase Admin SDK should bypass App Check. Thank you for finding and reporting this bug! Our team has identified the issue and currently working on a fix. We will use the following GitHub issue to track the progress https://github.com/firebase/firebase-admin-node/issues/1788

What permissions are required to GET_TEMPLATE for Firebase Remote Config?

I have a Firebase Cloud Function that invokes an Admin SDK service account to request the Remote Config template to compare changes to the previous version. I have this working in 2 other Firebase instances, but in this instance, I'm getting this error in the cloud function logs:
Function execution started
rejected token
"status": "PERMISSION_DENIED"
error encountered:
"error": {
"code": 403,
"message": "[AUTHORIZATION_ERROR]: User does not have the following permission: GET_TEMPLATE",
Function execution took 139 ms, finished with status: 'ok'
I have tried adding these permissions to the service account:
Cloud Functions Admin
Editor
Firebase Analytics Viewer
Firebase Remote Config Admin
Service Account Token Creator
I have also tried deploying this cloud function on 3 different service accounts and they all receive the same error.
Any suggestions would be most welcome.
This issue was resolved by adding the required permissions (cloudconfig.configs.get, cloudconfig.configs.update, and firebaseanalytics.resources.googleAnalyticsReadAndAnalyze) to the 'App Engine default service account'. This was confusing because we had thought that the default Firebase Admin SDK 'firebase-adminsdk-*****#projectId.iam.gserviceaccount.com' would be the default service account used to access the Remote Config GET_TEMPLATE. I know for certain we also tried manually specifying this service account when initializing the app in the Cloud Function. We even tried changing the 'Runtime service account' in the Cloud Function -> Edit -> 'Runtime, build, connections and security' settings to use the service account and that didn't work (which seems like that might be a bug). Only adding the correct permissions to the App Engine default service account seemed to work in this instance.
The only Remote Config permissions are cloudconfig.configs.get , cloudconfig.configs.updateand firebaseanalytics.resources.googleAnalyticsReadAndAnalyze
Firebase IAM Permissions lists the roles required for each product.
Also, it is important to note that "To authenticate a service account and authorize it to access Firebase services, you must generate a private key file in JSON format". Steps on how to do it and the same error similar to yours can be found in this thread
my solution was adding the Firebase Remote Config Admin role to the XYZ-123#appspot.gserviceaccount.com. not great that this is buried...!

Firebase invalid-credential 302 on initializeApp() in Cloud Functions Shell

In order to speed up my development/testing, I am trying to get started with using the Cloud Functions:shell instead of deploying --only functions everytime I make a tiny change.
I am getting an 'invalid-credentials' error though.
This is a basic working index.js file:
const express = require("express");
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const app = express();
app.get("/say_hi", (req, res) => console.log("HI!"));
exports.oauth = functions.https.onRequest(app);
Here is how I call the function from the shell in the CLI:
> firebase functions:shell
> oauth.get("say_hi")
Output is "HI!" written to the console, which is good.
The problem is when I try using admin.database() as in the following function:
app.get("/get_ref", (req, res) => admin.database().ref("users"));
This is the output error:
"code": "app/invalid-credential",
"message": "Credential implementation provided to initializeApp() via the 'credential' property failed to fetch a valid Google OAuth2 access token with the following error: 'Error fetching access token: 302 Found'"
I've read the docs for '302 found' code ad 'invalid-credentials', but this doesn't tell me much about solving the issue, so I started wondering if I need a service account to initialize the Admin SDK, but that would not make sense as when I run deploy the functions run seamlessly and I can read/write to/from the Realtime DB.
What is the issue here?
Have you tried to set the GOOGLE_APPLICATION_CREDENTIALS env variable? it probably would solved your issue: https://firebase.google.com/docs/functions/local-emulator
and looking at this Use of Service account credentials when using Cloud Functions Shell might be helpful in this case.

Firebase serve --only functions, admin.auth() locally does not work unless I manually set the key.json

I am trying to run cloud functions locally using firebase serve --only functions. It works if I manually specify the key.json that is downloaded when I create a service account through firebase console here: https://console.firebase.google.com/project/project-id/settings/serviceaccounts/adminsdk.
And then doing export GOOGLE_APPLICATION_CREDENTIALS=key.json. Why do I have to do this? Shouldn't firebase or gcloud handle this correctly? I thought credential in functions.config().firebase was ApplicationDefault. https://developers.google.com/identity/protocols/application-default-credentials. According to that, the env variable like above is checked first, so it works if that is there. But shouldn't #2 validate me correctly?
I have gcloud for app engine so I have done gcloud init and gcloud auth application-default login.
I initialize my app in index.js
const admin = require('firebase-admin');
const functions = require('firebase-functions');
admin.initializeApp(functions.config().firebase);
firebase serve --only functions deploys the functions fine. But when I execute one which uses this code admin.auth().getUser(uid)... it breaks with the following error:
Error: An internal error has occurred. Raw server response: "{"error":{"errors":[{"domain":"usageLimits","reason":"accessNotConfigured","message":"Access Not Configured. Google Identity Toolkit API has not been used in project 7640...50 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/identitytoolkit.googleapis.com/overview?project=7640...50 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.","extendedHelp":"https://console.developers.google.com/apis/api/identitytoolkit.googleapis.com/overview?project=7640...50"}],"code":403,"message":"Access Not Configured. Google Identity Toolkit API has not been used in project 7640...50 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/identitytoolkit.googleapis.com/overview?project=7640...50 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry."}}
The project number doesn't correspond to my firebase project or project id/number in console.cloud.google.com
Without gcloud, I get this error
Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: invalid_grant (Token has been expired or revoked.)". There are two likely causes: (1) your server time is not properly synced or (2) your certificate key file has been revoked. To solve (1), re-sync the time on your server. To solve (2), make sure the key ID for your key file is still present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If not, generate a new key file at https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk
similar to https://github.com/urish/firebase-server/issues/81
I already have a key file generated. I've tried deleting my existing ones and making new ones, the only solution is to download this key that is generated and manually export the environment variable
Is there any other way than that?
Technically yes, but you can build your code so that it uses a local key when testing locally, and uses the service key when running on the server.
For this I set the env var FB_SERVICE_ACCOUNT_KEY to the path of the json secret. When it is unset (i.e. deployed within Firebase functions), the standard init will be used.
Here is a basic example
if(process.env.FB_SERVICE_ACCOUNT_KEY){
var serviceAccount = require(process.env.FB_SERVICE_ACCOUNT_KEY);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: `https://${process.env.GCLOUD_PROJECT}.firebaseio.com`
});
}
else{
admin.initializeApp(functions.config().firebase);
}

How to connect to Firebase by Firebase Admin against a proxy?

Currently, I am using Firebase Admin SDK to connect a Firebase database in a NodeJS server side application.
But I do not find an option to connect Firebase via proxy settings, or it can detect my system HTTP_PROXY environment variable.
When I run the node script by node index.js, and got some timeout messages like this(I know in my work network, I can not connect to Firebase directly).
Error: Credential implementation provided to initializeApp() via the "credential
" property failed to fetch a valid Google OAuth2 access token with the following
error: "connect ETIMEDOUT 216.58.200.237:443".
at ....erver\node_modules\firebase-adm
in\lib\firebase-app.js:74:23
at process._tickCallback (internal/process/next_tick.js:103:7)
I also use browser to access the firebase console via proxy, it works.
But how to resolve this issue in NodeJS server side scripts?
This error also happens if the date and time on your host machine where you run NodeJS process is not set right. Make sure to keep the server time synced.
The full error message:
Error: Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Error fetching access token: invalid_grant (Invalid JWT: Token must be a short-lived token and in a reasonable timeframe)". The most likely cause of this error is using a certificate key file which has been revoked. Make sure the key ID for your key file is still present at https://console.firebase.google.com/iam-admin/serviceaccounts/project. If not, generatea new key file at https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk.
Faced with the exact same problem yesterday, got it solved.
Let's get it straight, you are getting this error because Google services are banned in your region, therefore you should access firebase through a proxy. Here's a blog explaining how it is done.
For this specific case, you should
Prepare a proxy server that allows you to access Google services, then
Install the https-proxy-agent package through npm or yarn, then
Include the proxy in your firebase app initilization code like this
import HttpsProxyAgent from 'https-proxy-agent';
import * as admin from 'firebase-admin';
...
const agent = new HttpsProxyAgent('url to your proxy server');
admin.initializeApp({
credential: admin.credential.applicationDefault(agent),
// Or any function you would like to use to provide your application's credentials
// But remember to include the proxy agent in the parameter
httpAgent: agent
});
But remember not to commit this change into your repo, since this problem appears specifically in regions without access to Google services.

Resources