Public service announcement: If your project uses Google Cloud Functions, and you’re running those functions using http triggers, please check to make sure that they are secure.

During development, it can be useful to open your Cloud Functions to “all users”. This means you can make changes to your Functions, and test them by calling them directly from a browser, Postman, or curl, and you don’t have to worry about authenticating at all.

But once you’ve deployed the Functions and finished development, your Functions might be called by anyone, anywhere, unless you’ve secured them. One simple test to see whether your Function is secure is to just open the http trigger in the address bar of a web browser. You should see a 403 error. If you can run the Function from a web browser, then anyone can.

To secure a Function, visit the Cloud Functions dashboard. You should see your Function in the dashboard, like this:

Google Cloud Functions dashboard

Google Cloud Functions dashboard

Click the Function name, and you’re taken to a details page with tabs for Metrics, Details, Source, Variables, Trigger, Permissions, Logs, and Testing. Click the Permissions tab, and check to see if “allUsers” is a principal. If so, you’ll want to remove this principal. Check “allUsers” and hit the “Remove” action button.

After doing this, it may take a minute or so to propagate the change. Eventually, you should find that running the Function from the browser results in a 403 error.

That’s great, but now you want to make sure that your intended user can still run the Function.

In particular, if you’re calling your Function from Node.js running an express.js server on Google App Engine, you’ll now need to authenticate the process. Otherwise, you’ll get a 403 error when trying to run the Function from express.js, just like you did in the browser.

Authenticating from Google App Engine is a multi-step project.

First, install google-auth-library in your App Engine (express.js) project by running npm install google-auth-library in the project root.

Now let’s assume that the code in your Node project uses https to call the function, something like this:

const cloudFunctionUrl = "https://region-project.cloudfunctions.net/myFunction";
https.get(cloudFunctionUrl, (resp) => {
    console.log("resp.statusCode " + resp.statusCode);
    ...

Now that you’ve tightened the security rules, running that code probably results in a statusCode of 403. Google App Engine needs to authenticate, but your code doesn’t do that. So replace https.get with the following code (there are lots of log statements to help you debug if you have any problems):

const { GoogleAuth } = require('google-auth-library');
...
const runCloudFunction = async () => {
    const auth = new GoogleAuth();
    const client = await auth.getIdTokenClient(cloudFunctionUrl);
    console.log("*** client *** " + JSON.stringify(client));
    const projectId = await auth.getProjectId();
    console.log("*** projectId *** " + projectId);
    console.log("*** Await requesting url ***");
    const res = await client.request({ url : cloudFunctionUrl });
    console.log("Done");
    console.log(res.data);
    ...
};

If the request fails, it may be because your Google App Engine doesn’t yet have permissions to call the Function. Check what’s printed out for client. You should see an object with lots of properties, including a key for email that looks something like firebase-adminsdk-abcde@project-name.iam.gserviceaccount.com.

If you check the Google Cloud IAM dashboard for your project, you should see that email address defined as a principal.

So next, trot on back to your Cloud Function dashboard, and add this email address as a principal. Click the “Add” action button, (it has a little person icon next to it). A panel slides open from the right. Tap into the “New principals” field, and start typing “firebase”. The form should autocomplete with your firebase admin email address as mentioned above. If it doesn’t, you can try entering the full email address that you saw in your debugging output. Assuming this is successful, move your mouse into the “Select a role” form input, and type in “Cloud Functions Invoker”. Hit the Save button. Check out the image below if anything about the process is not clear.

Google Cloud Functions Add Principal

Add Principal to GCF

It might take a short time (perhaps a minute) for this new principal to propagate. Make sure you’ve deployed your new App Engine code, and then test it by calling your express.js endpoint. Everything should just work now!