How to connect to Firebase by Firebase Admin against a proxy? - node.js

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.

Related

Google FCM firebase-admin initializeApp() error in K8S cluster

I am building NodeJs server and trying to leverage FCM for push notifications. Locally everything works as expected, but in the K8S cluster I receive the following error
FirebaseAppError: 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: Missing error payload".
errorInfo: {
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: Missing error payload".'
},
codePrefix: 'app'
I tried to initialize app without arguments admin.initializeApp(), keeping env var with path json key in a config object, the path is valid, the file is present, the env is printed out ok.
I tried like this const firebase = admin.initializeApp({ credential: admin.credential.applicationDefault(), }); which is obviuoely the same thing. I Tried to explicitly pass credential prop with json certificate like so const firebase = initializeApp({ credential: admin.credential.cert(cert) });
cert is present. I even tried to downgrade from firebase-admin from 10 to 9.
Node 12
please help
I found an issue. It was DNS settings of my K8S cluster. So it didn't have proper access to the outside world (check /etc/resolv.conf inside the pod)
Had to add dns settings to my deployment declaration like so
containers:
...
dnsPolicy: "None"
dnsConfig:
nameservers:
- 10.96.0.10 // for inner services lookup
- 8.8.8.8
searches:
- default.svc.cluster.local
- svc.cluster.local
- cluster.local

Calling `subscribeToTopic` from a locally run cloud function

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?

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);
}

Live Paypal Application fails to create token information from auth code but not in sandbox

In my Meteor web application I have managed to successfully implement some functions that allow me to take an authorization code granted from Paypal's authorizeUrl function via the RESTful Paypal Node SDK in the Sandbox in order to assign a refresh token to an existing user. However, now that we need to deploy it to a Live environment, we are running into problems because the Paypal API is complaining with the following error:
Error: Response Status: 400
This means the request was bad, due perhaps to a 'bad syntax.' However, I am using the correct live API credentials and the authorization URL endpoint is the correct one at https://www.paypal.com/signin/authorize
Unfortunately this bug doesn't occur when I am reverting back to my sandbox mode configuration.
As a follow up, trying to pass an invalid authorization code to my sandbox configuration endpoint provides me the appropriate error message 400 with Invalid authorization code whereas the live environment gives me no such help...
The latest paypal-debug-id I have on hand is 83d570f17357e
EDIT:
It turns out Paypal thinks client id or secret is null even though I have clearly provided the configuration with client_id, client_secret, openid_client_id and openid_client_secret. This confuses me even more
EDIT 2:
Using the same Live API credentials I can successfully set up a basic payment flow using the Rest SDK, but I cannot use open ID connect with the same client ID and secret. Maybe that has something to do with it?
After some extensive testing and wrestling with the API it was discovered that there was actually a configuration conflict going on when binding the Meteor.wrapAsync API call to an instance of paypal created from the node SDK.
Assuming my Meteor.wrapAsync call is this:
var wrapGetUserInfo = Meteor.wrapAsync(openIdCxn.userinfo.get.bind(openIdCxn.userinfo), function (err, userinfo) {
if (err) {
console.log(err);
throw new Meteor.Error(500, "Internal Server Error", err);
}
});
Then instead of doing
var userInfo = wrapGetUserInfo(returnObject.access_token, paypal);
I will do
var userInfo = wrapGetUserInfo(returnObject.access_token);
And then it no longer complains

Nodejs - ADAL package issue

I am using adal-node package in my Nodejs app for authenticating against Azure AD.
URL: https://www.npmjs.org/package/adal-node
I am using acquireTokenWithAuthorizationCode method to get the token and it works fine.
When my auth code expires, I want to refresh my token using the below.
authenticationContext.acquireTokenWithRefreshToken(_tokenData.refreshToken, authdata.clientId, authdata.resource, callback).
But when I run this code, its giving me the below error.
"Get Token request returned http error: 400 and server response: {"error":"invalid_request","error_description":"AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'
The method will not accept client secret as its argument, but still it complains that it needs a client secret.
Can you please help?
Thanks
Anil
Unfortunately, the library does not support your scenario right now. The function acquireTokenWithRefreshToken that you are using was intended for OAuth public clients that don't require a client secret, but your app is an OAuth confidential client which does.
I have filed the following issue in the GitHub repo to track the need to add a new method that would support your scenario.
https://github.com/AzureAD/azure-activedirectory-library-for-nodejs/issues/22

Resources