Error on Firebase generateEmailVerificationLink in GCP Cloud Function - javascript

I'm trying to set up a GCP Cloud Function to generate the email verification link using admin.auth().generateEmailVerificationLink, but it throws the error:
Error: Credential implementation provided to initializeApp() via the "credential" property has insufficient permission to access the requested resource. See https://firebase.google.com/docs/admin/setup for details on how to authenticate this SDK with appropriate permissions.
I was able to reproduce this error with the following Cloud Function code:
index.js:
const admin = require('firebase-admin');
admin.initializeApp();
exports.helloWorld = (req, res) => {
execute(res);
};
const execute = async (res) => {
const email = 'test#test.com';
const url = 'https://example.firebaseapp.com';
const link = await admin.auth().generateEmailVerificationLink(email, { url });
console.log(link);
res.status(200).send(link);
};
package.json:
{
"name": "sample-http",
"version": "0.0.1",
"dependencies": {
"firebase-admin": "^10.0.2"
}
}
My Firebase Admin Service Account (firebase-adminsdk-XXX#example.iam.gserviceaccount.com) has the roles:
Firebase Admin SDK Administrator Service Agent
Service Account Token Creator
I also viewed the API Key in Firebase Console, found it in GCP (Browser key (auto created by Firebase), and see that it has the following APIs selected:
Cloud Firestore API
Cloud Functions API
Firebase Installations API
Token Service API
Identity Toolkit API
I tried following the provided link (https://firebase.google.com/docs/admin/setup), but it seems specific to setting up admin outside of a GCP Cloud Function (see https://firebase.google.com/docs/admin/setup#initialize-without-parameters). I also read through https://firebase.google.com/docs/auth/admin/email-action-links, but there were no helpful details that I could find.
I tried using functions.https.onCall instead of the regular GCP exports.
I tried setting FIREBASE_CONFIG={"projectId":"example","storageBucket":"example.appspot.com","locationId":"<my-region>"} and GCLOUD_PROJECT=example as runtime env vars.

The issue was that because I was deploying the function on GCP (and not through Firebase), the actual service account that runs the function is not the one specified in the Firebase console (firebase-adminsdk-XXX#example.iam.gserviceaccount.com), it is instead the App Engine default service account:
At runtime, Cloud Functions defaults to using the App Engine default
service account (PROJECT_ID#appspot.gserviceaccount.com)
Source:
https://cloud.google.com/functions/docs/concepts/iam#access_control_for_service_accounts
So in this case, the call worked once I gave my account example#appspot.gserviceaccount.com the roles:
Firebase Admin SDK Administrator Service Agent
Service Account Token Creator
As a side note, no additional options were needed for admin.initializeApp() nor were the Runtime Environment Variables needed.

Since you did not deploy using the Firebase CLI and instead deployed with the Google Cloud Console, the FIREBASE_CONFIG environment variable was not deployed with it. See note. What you need to do is add a Runtime Variable of something like this:
Key: FIREBASE_CONFIG Value: {"projectId":"myawesomeproject","storageBucket":"myawesomeproject.appspot.com","locationId":"us-central"}
Key: GCLOUD_PROJECT value: myawesomeproject
I was able to test this using the Firebase CLI to deploy a function known to work and then compared the function variables and environment settings against a function deployed from the Google Cloud Console.

Related

I cannot deploy firebase cloud functions: Failed to load function definition from source, No "exports" main defined in D:\

I'm trying to implement Firebase Storage into my Cloud Functions app script. I've been running into a problem where my code won't deploy at this point in the script:
const app = express();
app.engine('hbs',engines.handlebars);
app.set('views','./views');
app.set('view engine','hbs');
admin.initializeApp(functions.config().firebase);
I run into this error:
Error: Failed to load function definition from source: Failed to generate manifest from function source: Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in D:\Documents\ShowtapeLibraryAdmin\node_modules\firebase\package.json
I am using Firebase 9.14, Functions 4.1.0, and Google-Cloud/Storage 6.70 as well as Firebase/app 0.8.4.
I've tried updating firebase, checking the code from the tutorial I used for cloud functions as well as updating any other packages and NPM
Here is the full index.js: https://pastebin.com/Q8J0LzCh
I do apologise if I have not properly formatted this question correctly, this is my first Stack Exchange post!
Firebase uses a modular, functional syntax starting from version V9.0.0. Try refactoring the code as shown below:
const { initializeApp } = require('firebase/app');
It seems you are trying to use Firebase SDK in a Cloud Function so you should ideally use the Firebase Admin SDK itself and not the client SDK as well. Try updating to latest version of Firebase Admin SDK and refactoring the code as shown below:
const { initializeApp } = require('firebase-admin/app');
const { getStorage } = require('firebase-admin/storage');
const storage = getStorage();
Checkout the documentation for Firebase Admin for Cloud Storage for more information.

How to emulate Firebase authentication without an API key

I'm building an open-source project using Firebase's JS SDK. My goal is to allow contributors to run the project locally using the Firebase emulator so that they don't need any real credentials. The Firebase emulator docs specify that "you can run the emulators without ever creating a Firebase project". That's exactly what I want!
After running firebase init, I wrote up the following code. It triggers a popup that allows users to sign in through GitHub:
import { initializeApp } from "firebase/app";
import { connectAuthEmulator, getAuth, GithubAuthProvider } from "firebase/auth";
const app = initializeApp({
projectId: "demo-project",
});
const auth = getAuth(app);
connectAuthEmulator(auth, "http://localhost:9099");
// When users sign in, we call the following method:
async function signIn() {
const githubAuth = new GithubAuthProvider();
await signInWithPopup(firebaseClientAuth, githubAuth);
}
The code above will trigger the following error:
Uncaught (in promise) FirebaseError: Firebase: Error (auth/invalid-api-key)
In the real world, I would call initializeApp() with an apiKey, but here I just want to emulate authentication. I've also tried not to call initializeApp() at all and call getAuth() without any arguments, but it triggers the same error.
Presumably, an API key requires creating a project, so is it actually possible to run the Firebase auth emulator without creating a Firebase project?
Yes it is.
A demo Firebase project has no real Firebase configuration and no live resources. These projects are usually accessed via codelabs or other tutorials.
Project IDs for demo projects have the demo- prefix.Firebase Docs
Run firebase emulators:start --project demo-[ANYTHING_YOU_WANT]
For example firebase emulators:start --project demo-test-project
is it actually possible to run the Firebase auth emulator without creating a Firebase project?
No, you need to have the emulator connected to a project. You'll notice in the setup instructions you are required to run firebase init to choose your project and the products you want to use in that project.

Using the AWS javascript SDK, V3, is there a credentials provider chain equivalent?

I'm migrating from V2 to V3 of the javascript SDK for AWS, using NodeJS. Our application needs to check for credentials in a couple places. Previously we used the Credential Provider Chain but I cannot find the equivalent in V3. I need to look in the shared INI file (SharedIniFileCredential) when my script runs locally but the script also runs in kubernetes so (I think) I also need roleAssumerWithWebIdentity. How do I use a credential chain in V3?
The module #aws-sdk/credential-provider-node provides a default credential provider similar to what you're looking for:
It will attempt to find credentials from the following sources (listed in order of precedence):
Environment variables exposed via process.env
SSO credentials from token cache
Web identity token credentials
Shared credentials and config ini files
The EC2/ECS Instance Metadata Service
Here's an example from their page:
const { getDefaultRoleAssumerWithWebIdentity } = require("#aws-sdk/client-sts");
const { defaultProvider } = require("#aws-sdk/credential-provider-node");
const { S3Client, GetObjectCommand } = require("#aws-sdk/client-s3");
const provider = defaultProvider({
roleAssumerWithWebIdentity: getDefaultRoleAssumerWithWebIdentity,
});
const client = new S3Client({ credentialDefaultProvider: provider });

How to use firestore emulator from client

I want to test locally my firebase functions.
These functions make firestore queries.
So i start the emulator firebase emulators:start and in my client i use firebase.functions().useFunctionsEmulator('http://localhost:5001').
My functions work well when i call them in my client. I can read/write data inside the firestore emulator.
The problem :
I want to read the firestore emulator data directly inside my client, like :
firebase.firestore().collection('tests').get().then(tests => {
console.log( tests.docs.map(test=>test.map) )
})
but i can't find how to set the firestore emulator inside my client.
here what i tried:
1) Firestore setting
firebase.firestore().settings({
host:'http://localhost:8080',
ssl:false
})
result :
i get #firebase/firestore: Firestore (6.3.5): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds. inside my client console.
The http request returns 'Not found'
2) Set the emulator url inside my firebaseConfig
var firebaseConfig = {
// ...
databaseURL: "http://localhost:8080",
// ...
}
firebase.initializeApp(firebaseConfig)
in this case, the remote server (https://firestore.googleapis.com..) is requested.
So i want to setup one of these two cases :
1) Using the remote firestore inside my functions emulators
OR
2) Using the local firestore emulator inside my client code.
Anyone has already done this ?
Install the testing lib
npm i -D #firebase/testing
Setup and start the emulator in another terminal:
firebase setup:emulators:firestore
firebase serve --only firestore
Setup the tests
const firebase = require("#firebase/testing");
// Helper function to setup test db
function authedApp(auth) {
return firebase
.initializeTestApp({ projectId: FIRESTORE_PROJECT_ID, auth })
.firestore();
}
// Setup methods
beforeEach(async () => {
// Clear the database between tests
await firebase.clearFirestoreData({ projectId: FIRESTORE_PROJECT_ID });
});
// Clean up apps between tests.
afterEach(async () => {
await Promise.all(firebase.apps().map(app => app.delete()));
});
Run the tests
it("should retrieve correct item", async () => {
// Init test db
const db = authedApp(null);
// Manually add item to collection
const ref = await db.collection(COLLECTION_NAME).add({name: 'test item'});
// Fetch item by id
const resp = await db.collection(COLLECTION_NAME).doc(ref.id).get();
// test the output
expect(resp).toBeDefined();
expect(resp).toEqual(expect.objectContaining({name: 'test item'}));
});
Of course your particular setup and circumstances will differ, but this at least should give you a general idea. More info: https://firebase.google.com/docs/rules/unit-tests
Note from 'Test your Cloud Firestore Security Rules'
Data written to the Cloud Firestore emulator is held in memory until
the emulator is stopped. If the emulator is run continuously, this may
have an impact on test isolation. To ensure that data written in one
test is not read in another, either explicitly clear your data with
clearFirestoreData, or assign a different project ID for each
independent test: when you call firebase.initializeAdminApp or
firebase.initializeTestApp, append a user ID, timestamp, or random
integer to the projectID.
Edit: I wrote a blog post a while back, which goes into more detail about the subject.
I had the same problem too. I found the following example, which looks like its still work in progress:
https://github.com/firebase/quickstart-nodejs/tree/master/firestore-emulator/browser-quickstart
They didn't use #firebase/testing directly in their example. When I did try to embed #firebase/testing in my webpack code, it tries to connect via grpc, which attempts to do fs.existsSync, which doesn't exist in webpack browser context. They prefer to enable the functionality via WebChannel instead.
Some caveats as of Nov 2019:
You might see errors in the console connecting to localhost:8080 with the X-Goog-API. I am not sure what is that for.
You might get the following error: #firebase/firestore: Firestore (6.3.5): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
Despite having those errors, my functions were still able to connect to the local firestore.
Versions I had at time I was testing:
firebase-js-sdk 6.3.1
firebase-tools 7.3.1
Update 11/18:
I raised an issue within quickstart-nodejs github and it seems I just needed to use the latest versions of everything.
Versions:
firebase-js-sdk v7.4.0
firebase-tools v7.8.0
Ok i found how to do it :
1) launch the functions emulator locally:
set GOOGLE_APPLICATION_CREDENTIALS=./privatekey.json && firebase serve --only functions
2) then client side:
if (process.env.NODE_ENV === 'development') {
firebase.functions().useFunctionsEmulator('http://localhost:5001')
}
Okay, this is trivial... In your firestore cient config you should have provided the host, not the origin for firestore (the protocol is set using the ssl parameter):
firebase.firestore().settings({
host: 'localhost:8080',
ssl: false
})
At least this solved it for me when I had the exact same error.
Just FYI to anyone who's reading this - if you run into problems with the firestore client, you can use debug level logging, just set firebase.firestore.setLogLevel('debug'). Had the OP done that, he might have noticed that firebase is accessing firestore at http://http://localhost:8080/...
Define the FIRESTORE_EMULATOR_HOST environment variable
If you are using a library that supports the FIRESTORE_EMULATOR_HOST environment variable, run:
export FIRESTORE_EMULATOR_HOST=localhost:8080
or just add FIRESTORE_EMULATOR_HOST=localhost:8080 to your .env file

Setup of firebase-admin sdk in javascript file

My end goal is to get user email, using user's id. So far I figured out I need to use the firebase admin SDK. Right now I have this code:
var admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://zdajtotestdb.firebaseio.com"
});
export default admin;
The serviceAccountKey is the file I downloaded from my firebase project here (Sorry it's in Polish)
I tried using this video as a tutorial/reference
https://youtu.be/WtYzHTXHBp0
And right now, when I try to do anything I get this in the npm start console:
20:22-48 Critical dependency: the request of a dependency is an expression
./node_modules/google-gax/node_modules/grpc/src/grpc_extension.js
32:12-33 Critical dependency: the request of a dependency is an expression
./node_modules/hash-stream-validation/index.js
Module not found: Can't resolve 'fast-crc32c' in '/home/iron/Documents?>/Projects/zdajto-admin-panel/node_modules/hash-stream-validation'
Search for the keywords to learn more about each warning.
To ignore, add // eslint-disable-next-line to the line before.
And this in the browser console:
Error fetching user data: Error: "Credential implementation provided to initializeApp() via the "credential" property failed to fetch a valid Google OAuth2 access token with the following error: "Failed to parse access token response: Error: Error while making request: XHR error. Error code: undefined"."
What am I missing?
Firebase Admin SDK is not available for Javascript, here the prerequisites:
https://firebase.google.com/docs/admin/setup#prerequisites
So, for example, you cannot operate as admin in a web page for security reason, but only in a privileged environment (like a server).
I'm creating a web administration interface for my project, where admin operations are performed in Cloud Functions.

Categories