firebase admin sdk authentication fails locally with getaddrinfo ENOTFOUND - firebase

Having a local developed cloud function and started it locally via:
firebase emulators:start
The cloud function is here:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL:"https://test1-xxx.firebaseio.com",
});
I get:
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: Error while making request: getaddrinfo ENOTFOUND metadata.google.internal. Error code: ENOTFOUND".
at FirebaseAppError.FirebaseError [as constructor] (/Users/dgang/study/test/functions/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAppError.PrefixedFirebaseError [as constructor] (/Users/dgang/study/test/functions/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseAppError (/Users/dgang/study/test/functions/node_modules/firebase-admin/lib/utils/error.js:122:28)
at /Users/dgang/study/test/functions/node_modules/firebase-admin/lib/firebase-app.js:121:23
at processTicksAndRejections (internal/process/task_queues.js:89:5) { 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: Error while making request: ' +
'getaddrinfo ENOTFOUND metadata.google.internal. Error ' +
'code: ENOTFOUND".' }, codePrefix: 'app' }
I defined GOOGLE_APPLICATION_CREDENTIALS as needed but this does not help.
I tried to google the issue but without avail.
What needs to be fixed here?
Update:
When i require the service account explicitely it works:
var serviceAccount = require("service-account.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL:"https://test1-xxx.firebaseio.com",
});
but this is a dangerous option

Related

Firebase 9 - cant login in authentication emulator

I can't login using authentication emulator. The emulators set up properly, the emulator UI works well, also the functions interact with storage and firestore.
Firebase config:
...
const auth = getAuth();
const db = getFirestore();
// enable emulators
if (true) {
connectFirestoreEmulator(db, "127.0.0.1", 8081);
connectAuthEmulator(auth, "https://127.0.0.1:9099");
connectStorageEmulator(storage, "127.0.0.1", 9199);
connectFunctionsEmulator(functions, "127.0.0.1", 5001);
}
Login
...
import {
GoogleAuthProvider,
onAuthStateChanged,
signInWithCredential,
signOut,
signInWithEmailAndPassword,
} from "firebase/auth";
signInWithCredential(
GoogleAuthProvider.credential(
'{"sub": "abc123", "email": "foo#example.com", "email_verified": true}'
)
)
Error: Possible Unhandled Promise Rejection (id: 0):
TypeError: undefined is not an object (evaluating 'credential._getIdTokenResponse')
I am following the instructions of the firebase documents:
https://firebase.google.com/docs/emulator-suite/connect_auth
I added the env variable FIREBASE_AUTH_EMULATOR_HOST="http://127.0.0.1:9099"
The payload above '{"sub": "abc123", "email": "foo#example.com", "email_verified": true}' also coming from the documentation.
Also note, that without emulator - so connecting to my firebase project directly - everything works well, its just the emulator that I cannot make work.

Firebase admin.auth().createCustomToken(userId) occurred "auth/internal-error: Request contains an invalid argument"

I'm using firebase-admin on a node server
Initializing the admin app works fine:
const admin = require("firebase-admin");
admin.initializeApp({
serviceAccountId: '***.apps.googleusercontent.com'
});
Here's the code I create a custom token and send back to client.
const userId = 'some-uid';
admin.auth().createCustomToken(userId).then((customToken) => {
console.log(customToken);
res.send(customToken);
}).catch((error) => {
console.log(error);
res.status(500).send(error);
});
The error is occurred at line admin.auth().createCustomToken(userId):
code: 'auth/internal-error',
message: 'Request contains an invalid argument.; Please refer to https://firebase.google.com/docs/auth/admin/create-custom-tokens for more details on how to use and troubleshoot this feature. Raw server response: "{"error":{"code":400,"message":"Request contains an invalid argument.","status":"INVALID_ARGUMENT"}}"'
I followed the links below and I don't find any address to solve this
https://firebase.google.com/docs/auth/admin/create-custom-tokens
https://firebase.google.com/docs/auth/admin/errors
I changed the way to initialize the admin, this function works properly.
const admin = require("firebase-admin");
const serviceAccount = require("./serviceAccountKey.json")
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: 'https://<DATABASE_NAME>.firebaseio.com',
serviceAccountId: '***.apps.googleusercontent.com'
});
For more detail please refer to these links:
Generating a custom auth token with a cloud function for firebase using the new 1.0 SDK
https://firebase.google.com/docs/admin/setup
I don't know why it works, cause following this issue https://github.com/firebase/firebase-admin-node/issues/224#issuecomment-390424993, the "creating custom tokens without a service account" feature is implemented. But these changes work for me. Hope it helps!!!

Firebase functions with nodemailer POST request: Error: Process exited with code 16

I'm running a Firebase cloud function whose purpose is to handle the backend of a "contact me" form. The Express framework is being used for middleware functions. When the submit button is clicked, a POST request is made to the /submit endpoint.
index.js in the functions folder is as below:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const bodyParser = require("body-parser");
const express = require("express");
const emailRoute = require("./routes/email");
// Initialize Firebase in order to access its services.
admin.initializeApp();
const app = express();
// Automatically allow cross-origin requests.
app.use(cors({ origin: true }));
app.use(bodyParser.urlencoded({extended: false}));
app.use(emailRoute);
// Expose Express API as a single Cloud Function.
exports.app = functions.https.onRequest(app);
The imported router from email.js is as follows:
const nodemailer = require("nodemailer");
const express = require("express");
const router = express.Router();
router.post("/submit", (req, res) => {
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: "email#gmail.com",
pass: "password",
},
});
const myEmail = {
to: "myemail#gmail.com",
subject: `A new message from ${req.body.name}`,
text: `${req.body.name} sent the following message:
\n\n ${req.body.message}
\n\n Senders email: ${req.body.email}`,
};
const sendersEmail = {
to: req.body.email,
subject: "A copy of your message to me",
text: `You just sent me the following message:\n\n${req.body.message}`,
};
console.log("SUBMIT REQUEST PROCESSING");
transporter.sendMail(myEmail);
transporter.sendMail(sendersEmail);
res.redirect("/#contact");
console.log("PROCESSING COMPLETE");
});
module.exports = router;
There is no issue when running this in the local environment - the email gets sent to both parties. However, when run in the hosted environment as a Firebase function the following error is thrown: Error: Process exited with code 16 (as displayed in the function logs section of the Firebase console). A previous SO answer indicates an uncaughtException or unhandledRejection.
Before the error, both console.log() statements are logged. Then the function finishes with a 302 status code (as it does when run locally and successfully). After that, there is the unhandled rejection followed by Error: Invalid login and a link to my Google account and a statement "Please log in via your web browser and then try again".
Could this be a Firebase security measure against automated mailing that nodemailer is attempting to execute?
I needed to both enable less secure apps and display unlock captcha on the gmail account, only the former of which I had previously done.
I also needed to set the gmail.email and gmail.password Google cloud environment variables. This can be done with the following shell command: firebase functions:config:set gmail.email="myusername#gmail.com" gmail.password="secretpassword"
A great resource I found on this is an official Firebase function sample that utilises Nodemailer, where the above is covered.
The issue is in logging to your Google account. Have you enabled less secure apps in your Google account? Or better I would recommend to use some other authentication, as described here:
https://support.google.com/a/answer/176600?hl=en

How to fix 401 error in listing users after Firebase update to 8.2.0

I'm creating an app that lists users and I'm doing that through Firebase Functions. After a recent update I've been forced to update firebase-admin from 7.4.0 to 8.2.0 but now I'm getting the following error:
Unhandled error { Error: //developers.google.com/identity/sign-in/web/devconsole-project. Raw server response: "{"error":{"code":401,"message":"Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.","errors":[{"message":"Invalid Credentials","domain":"global","reason":"authError","location":"Authorization","locationType":"header"}],"status":"UNAUTHENTICATED"}}"
I've already done the following:
Use service account
Follow instructions here but I'm still seeing the errors above.
Edit: Here's the code
import admin from "firebase-admin";
import functions from "firebase-functions";
const app = admin.initializeApp();
const auth = admin.auth(app);
export const listUsers = functions.https.onCall(async () => {
const users = await auth.listUsers();
return users.users.map(user => {
return user.toJSON();
});
});

File upload with filestreams and Firebase cloud functions + cloud storage

I've written the following code for my QR file upload using firebase cloud functions
const functions = require('firebase-functions');
const qrcode = require('qrcode')
const admin = require('firebase-admin');
const spawn = require('child-process-promise').spawn;
const serviceAccount = require("./secret.json");
const gcs = require('#google-cloud/storage')();
admin.initializeApp(functions.config({
credential: admin.credential.cert(serviceAccount),
storageBucket: "https://SECRET.firebaseio.com"
}));
exports.qrcode = functions.https.onRequest((req, res) => {
const storage = admin.storage().bucket();
const dummyFile = storage.file('dummy.png')
new Promise ((resolve, reject) => qrcode.toFileStream(
dummyFile.createWriteStream()
.on('finish', resolve)
.on('error', reject),
'DummyQR'))
.then(console.log("success")) //Doing stuff here later
.catch(console.log)
res.send("<script>window.close();</script>")
});
According to the docs I should be able to connect to the bucket by simply calling admin.storage().bucket(); (https://firebase.google.com/docs/storage/admin/start) however I get the following error:
Error: Error occurred while parsing your function triggers.
Error: Bucket name not specified or invalid. Specify a valid bucket name via the storageBucket option when initializing the app, or specify the bucket name explicitly when calling the getBucket() method.
and so I'm stuck and not sure how to proceed. If I try to manually enter the bucket admin.storage().bucket("https://SECRET.firebaseio.com"); I get the error
{ ApiError: Not Found
at Object.parseHttpRespBody (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:193:30)
at Object.handleResp (/user_code/node_modules/firebase-admin/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:131:18)
at /user_code/node_modules/firebase-admin/node_modules/#google-cloud/storage/node_modules/#google-cloud/common/src/util.js:496:12
at Request.onResponse [as _callback] (/user_code/node_modules/firebase-admin/node_modules/retry-request/index.js:195:7)
at Request.self.callback (/user_code/node_modules/firebase-admin/node_modules/request/request.js:185:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request.<anonymous> (/user_code/node_modules/firebase-admin/node_modules/request/request.js:1157:10)
at emitOne (events.js:96:13)
at Request.emit (events.js:188:7)
code: 404,
errors: [ { domain: 'global', reason: 'notFound', message: 'Not Found' } ],
response: undefined,
message: 'Not Found' }
I had this same problem. I just added the storageBucket name when initializing the app, and if you are using the getSignedUrl method you need to include a service account, otherwise that url it's gonna get expired after a week (as it was in my case).
const serviceAccount = require('/your/service/account/key/path');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
storageBucket: "your-storage-bucket-name.appspot.com",
});
don't forget to update your firebase functions with
firebase deploy --only functions
on the command line
UPDATE 6 APRIL 2020
Signed URLs actually expire after 7 days. If you need URLs for more time than that, you should read this answer and this thread
It looks like you're not initializing the admin SDK correctly. Just call initializeApp with no parameters to get all the correct defaults:
admin.initializeApp();
This will use the default service account provided by Cloud Functions for your project. This account has permission to do most of what you need to do in your function without any additional configuration.
In my case it was similar issue to this one (Unable to use cloud storage without specifying storageBucket when initialising app with firebase-admin).
I had following setup:
const admin = require("firebase-admin")
admin.initializeApp()
export * from "./api/api"
export * from "./cron/cronJobs"
export * from "./posts/postsCloudFunctions"
Changed it to:
const admin = require("firebase-admin")
export * from "./api/api"
export * from "./cron/cronJobs"
export * from "./posts/postsCloudFunctions"
admin.initializeApp()
And it seems the issue is initializeApp() was not being called before accessing the storage bucket.
I have same issue while uploading to image to firebase storage:
Error: Bucket name not specified or invalid. Specify a valid bucket name via the storageBucket option when initializing the app, or specify the bucket name explicitly when calling the getBucket() method.
ndlers\users.js:130:8)
at Busboy.emit (events.js:189:13)
at Busboy.emit (E:\react-project\socialape-functions\functions\node_modules\busboy\lib\main.js:37:33)
at E:\react-project\socialape-functions\functions\node_modules\busboy\lib\types\multipart.js:52:13
at process._tickCallback (internal/process/next_tick.js:61:11)
Then i add storageBucket to admin function
var admin = require("firebase-admin");
var serviceAccount = require("your service account key file path);
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://your-app-1234.firebaseio.com",
storageBucket: "your-storage-bucket-name given in firebase storage"
});
const db = admin.firestore();
module.exports = { admin, db };
After adding bucket to admin functions it works.
You should change
storageBucket: "your-storage-bucket-name given in firebase storage"
to
storageBucket: "<BUCKET_NAME>.appspot.com".
You can find BUCKET_NAME as the picture below:

Resources