The following is the client side code to call the cloud function:
var getShippingRate = firebase
.functions()
.httpsCallable("shippo-generateShippingRate");
getShippingRate({ address: shippo })
.then(function(result) {
// Read result of the Cloud Function.
console.log("THE SHIPPING RATES", result.data.shipment);
})
.catch(function(error) {
// Getting the Error details.
console.log("ERROR SHIPPING: ", error);
var code = error.code;
var message = error.message;
var details = error.details;
});
The cloud function:
exports.generateShippingRate = functions.https.onCall(async (data, context) => {
const customer_address = data.address;
return generateShipmentObject(customer_address);
});
generateshipmentObject returns this:
shippo.shipment.create(
{
address_from: addressFrom,
address_to: addressTo,
parcels: [parcel],
async: true
},
(err, shipment) => {
// asynchronously called
if (err) {
return { error: err };
} else {
return { result: shipment };
}
}
I get the standard CORS Error, but a callable Cloud Function should handle this automatically:
Access to fetch at ... from origin 'http://localhost:5000' has been blocked by CORS policy:
EDIT
I'm using firebase serve --only hosting to test on localhost.
The Cloud Functions are deployed with firebase deploy --only funtions
I'm calling other similar Cloud Functions on the same site which do not have that issue.
Temp fix:
In the cloud console functions page, select a function to show the info panel. In the permissions tab, select ADD MEMBER. In the new members field, type allUsers. In the roles drop down, select cloud functions, then cloud functions invoker, and save.
It actually sort of makes sense for a function to have restricted permissions when it's first created, however I'm used to the default permissions being present, so it's a bug (or new feature) that definitely threw me off. Of course this doesn't fix the underlying problem, but hope it helps.
Related
I am trying to send a POST request via axios andfirebase cloud function while the body of the request in axios contains data from changes occured in realtime db using the functions but everytime I deploy my code I get the error:
Function failed on loading user code. This is likely due to a bug in the user code.
Error message: Error: please examine your function logs to see the error cause:
https://cloud.google.com/functions/docs/monitoring/logging#viewing_logs. Additional
troubleshooting documentation can be found at
https://cloud.google.com/functions/docs/troubleshooting#logging. Please visit
https://cloud.google.com/functions/docs/troubleshooting for in-depth troubleshooting
documentation.
I am quite new to firebase functions so I really don't know what I am doing here even after having some research.
Code:
const functions = require("firebase-functions");
const axios = require("axios");
exports.newNodeDetected = functions.database
.ref("Orders/{userId}/{customerId}/{ordernum}/customername")
.onCreate((snapchot, context) => {
const order = snapchot.val();
const userId = context.params.userId;
console.log(userId + "AND CUS NAME IS" + order);
axios.post("http://something.com/data.php", {
username: userId,
title: "ttl",
message: "msg",
})
.then(function(response) {
console.log(response);
})
.catch(function(error) {
console.log(error);
});
});
Just in case anyone had the same issue. I realized I was running my npm commands outside the functions folder, therefore the package.json wasn't getting edited and my libraries were not really getting downloaded.
Always be careful to use npm inside the functions folder!!
When deploying Firebase Functions using the Firebase CLI, they are configured so that the Cloud Functions Invoker permission is granted to allUsers. With such a setting the code below functions as expected.
The Cloud Functions Invoker permission can also be granted to allAuthenticatedUsers. However, when I implement this change for addMessage, I only ever get a UNAUTHENTICATED error response using the code below.
Why won't allAuthenticatedUsers work for this Firebase Cloud Function?
Note: This Q&A is a result of a now-deleted question posted by Furkan Yurdakul, regarding why allAuthenticatedUsers wasn't working with his Firebase Callable Function for his Firebase app
MWE based on the documentation, with addMessage defined here:
firebase.auth().signInAnonymously() // for the sake of the MWE, this will normally be Facebook, Google, etc
.then((credential) => {
// logged in successfully, call my function
const addMessage = firebase.functions().httpsCallable('addMessage');
return addMessage({ text: messageText });
})
.then((result) => {
// Read result of the Cloud Function.
const sanitizedMessage = result.data.text;
alert('The sanitized message is: ' + sanitizedMessage);
})
.catch((error) => {
// something went wrong, keeping it simple for the MWE
const errorCode = error.code;
const errorMessage = error.message;
if (errorCode === 'auth/operation-not-allowed') {
alert('You must enable Anonymous auth in the Firebase Console.');
} else {
console.error(error);
}
});
Simply put, if the ID token passed to a Cloud Function represents a Google account (that used Google Sign-In through Firebase or Google itself), it works, otherwise, it doesn't.
Think of allAuthenticatedUsers as allAuthenticatedGoogleUsers instead of allAuthenticatedFirebaseUsers.
Background Information
For Callable Firebase Functions used with the Firebase Client SDKs, you will normally grant allUsers the permission to call it (the default setting Firebase CLI deployed functions).
A valid authenticated client request for a Google Cloud Functions must have an Authorization: Bearer ID_TOKEN header (preferred) or ?access_token=ID_TOKEN. Here, ID_TOKEN is a signed-in Google user's ID token as a JWT.
When Firebase Client SDKs call a Callable Function, they set the Authorization header for you with the current user's ID token (if the user is signed in, here). This is done so that the user's authentication token can be used in the context parameter of onCall() functions. Importantly though, a Firebase user's ID token doesn't always represent a Google user which makes it incompatible with allAuthenticatedUsers.
Because of this, you will have to gate your callable function in your code by checking context.auth and it's properties like below.
export const addMessage = functions.https.onCall((data, context) => {
if (!context.auth) {
// Throwing a HttpsError so that the client gets the error details.
throw new functions.https.HttpsError(
'failed-precondition',
'The function must be called while authenticated.'
);
}
// a valid user is logged in
// do work
});
Addendum on 403 Forbidden Errors
If your function is consistently throwing a 403 error after being deployed, this is likely because you are using an outdated copy of the Firebase CLI, as highlighted in the documentation:
Caution: New HTTP and HTTP callable functions deployed with any Firebase CLI lower than version 7.7.0 are private by default and throw HTTP 403 errors when invoked. Either explicitly make these functions public or update your Firebase CLI before you deploy any new functions.
I have a simple firebase functions script setup (running firebase-admin version 8.0 and firebase-functions version 2.3.1):
const functions = require('firebase-functions');
const cors = require('cors')({
origin: true,
});
//Gets and returns a user's ip address
exports.getIPAddress = functions.https.onRequest((req, res) => {
let ipAddress = req.headers['fastly-client-ip'] || req.connection.remoteAddress;
ipAddress = ipAddress.toString();
console.log('Fetched IP Address: ' + ipAddress);
return cors(req, res, () => {
res.status(200).send(ipAddress);
});
});
The function's goal is simply to return to user's IP address. It logs fine in the functions console, no errors.
Here is the client code:
var getIPAddress = mainFirebase.functions().httpsCallable('getIPAddress');
function testIP() {
getIPAddress().then(function(result) {
console.log(result.data.text)
});
}
However, the console says that 'result' is not a valid JSON object.
I've tried using https.onCall which somebody else on the internet recommended, however, the console says that function doesn't exist.
Any help getting the response to work properly would be greatly appreciated!
Your function is a regular HTTP type function. However, your client code is attempting to call it as if it were a callable type function. That's not going to work. If you want to invoke a callable type function, you'll have to implement the function according to the documentation.
If you need to keep the function as an HTTP type function, you can't use the Firebase client SDK to invoke it. Just invoke it as if it were any other type of HTTP endpoint.
For Callable functions. You need to create a function like:
exports.addMessage = functions.https.onCall(
async (data, context) => {
// context contains the user info.
}
);
And on your front-end you can call them like:
firebase.functions().httpsCallable('addMessage');
addMessage({text: messageText}).then(function(result) {
// Read result of the Cloud Function.
var sanitizedMessage = result.data.text;
}).catch(function(error) {
// Getting the Error details.
var code = error.code;
var message = error.message;
var details = error.details;
// ...
});
As you are calling an https message. You can also use the SDK to call https methods. But make sure you are handling CORS on your server.
In your client. Just use the http client.
this.http.post method with the function url.
I am trying to upload files to google cloud storage using a cloud function which is triggered by HTTP. However when the cloud function sends the file to be uploaded I often (although not always) get the following error
ERROR uploading to storage: { ApiError: Anonymous caller does not have storage.objects.create access to bucket_name/folder/test.jpg.
I am not sure why this error occurs - and why only some of the time
Here is the code:
const storage = require('#google-cloud/storage')();
function uploadToStorage(filepath, folder, filename) {
const options = {
destination: bucket.file(`${folder}/${filename}`),
public: false,
resumable: false
};
storage
.bucket(BUCKET_NAME)
.upload(filepath, options)
.then(function () {
console.log(`${filename} uploaded to ${BUCKET_NAME}`);
})
.catch((err) => {
console.error('ERROR uploading to storage: ', err);
});
}
Thanks
I had the same error after adding a return statement at the end of my function that performed file deletes on storage objects. This is what I was doing:
Make a database call to get some data
Once that request comes back, delete some files out of cloud storage (GCS)
The code structurally looked like this:
deleteStuffOutStorage() {
admin.firestore().doc(`My-doc-ref`).get()
.then(snapshot => {
// Do the deleting here {Interacting with GCS}
return deleteFile(snapshot.data().path); // Deletes file
})
.then(success => {
// Worked
})
.catch(error => {
// Error = ApiError: Anonymous caller does not have storage.objects...
})
return; // This statement was creating the problems
}
When I removed the return statement, I no longer got the error. I thought in my case it may have something to do with firebase-admin object instance getting deallocated and re-allocated between asynchronous operations (steps 1 and 2 above), or at least its GCS auth token?
All FCF instances should have access to GCS via a service account that is auto-generated. You can confirm this in the GCP console : https://console.cloud.google.com/iam-admin/serviceaccounts/
From the code snippet you posted I can't see anything that would cause the same issue I was getting, but maybe have a think about any time-based events that could cause this behaviour. That may explain the inconsistent behaviour you elude to.
Hope that's some sort of help.
I am getting an error stating external network is not accessible, which makes sense as I am on the free tier of Firebase. But I thought Firebase services were included in the free tier, and as such, I should be able to use FCM.
Here is the code I am using for my index.js for the functions.
var functions = require('firebase-functions');
var admin = require("firebase-admin");
admin.initializeApp(functions.config().firebase);
exports.buttonPress = functions.https.onRequest((req, res) => {
let testToken = "TOKEN";
let payload = {
data: {
type: req.body.type
}
};
admin.messaging().sendToDevice(testToken, payload)
.then(function (response) {
...
})
.catch(function (error) {
...
});
});
firebaser here
Billing account not configured. External network is not accessible and quotas are severily limited. Configure billing account to remove these restrictions.
This message now shows up for any Cloud Functions that are invoked from projects that are on the free tier. It doesn't mean that any calls have actively been blocked, just they calls to external services will be blocked for this project.
We're looking if we can get the message removed.
For Free tier account, Firebase has imposed a restriction on accessing external service that is not within google's network.
To get to the root cause of the problem just go to the Firebase console and check your functions's log. The log will show exactly what service or packages you installed is trying to make external HTTP request.
To sent FCM through cloud functions, you can use the code below.
Check log if you are getting right tokens.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
exports.sendNotification = functions.firestore
.document('/users/{documentId}')
.onWrite((change, context) => {
console.log("DOCUMENT ID : " + context.params.documentId);
//Get all data
const payload = {
notification: {
title: 'Test title!',
body: `${userName} sent you a following request.`
// icon: follower.photoURL
}
};
admin.messaging().sendToDevice(followedFCMToken, payload)
.then(function (response) {
console.log("Push response : " + response);
return response
})
.catch(function (error) {
console.error("Error in sending push");
});
});