How to invoke Cloud Functions from Firebase Hosting? - firebase

I have static content on firebase-hosting - where i have routing eg. ~/xxx - is possible to invoke firebase-function when someone enter this path ?

Yes. This is a recently announced feature at Google's 2017 IO conference. You can create a rewrite alias in your firebase.json file which points to a cloud function via your domain name. This is the section of the documentation which clearly describes how it is done.

Calling a cloud function directly from a hosted web application or mobile application is now possible from the below firebase version.
Firebase SDK for iOS 7.4.0 and above
Firebase SDK for Android 19.2.0 and above
Firebase JavaScript SDK 8.2.3 and above
The cloud function must be deployed in firebase and call below code from the client-side.
var showMessage = firebase.functions().httpsCallable('showMessage');
showMessage({ text: messageText })
.then((result) => {
// Read result of the Cloud Function.
var sanitizedMessage = result.data.text;
});
For more details, Click here (https://firebase.google.com/docs/functions/callable#web_1)

Related

How to get firebase App Check working with cloud firestore in a web app

I try to get App Check working in my vue.js pwa. Using the latest firebase sdk and by following steps here https://firebase.google.com/docs/app-check/web/recaptcha-provider
My api key (https://console.cloud.google.com/apis/credentials/key..) is not limited to any API restrictions. I did add some Application Restrictions on https tho including my project domains.
Everything works fine till i activate appCheck with recaptcha v3 and i get following console errors:
FirebaseError: [code=unknown]: Fetching auth token failed: AppCheck: Fetch server returned an HTTP error status. HTTP status: 403. (appCheck/fetch-status-error)
Further more the app can't get any firebase data or auth. I tried in several browsers and without any vpn stuff.
In my already installed pwa the App Check error occurs but connection to firebase still works..
Without App Check activated it both works without an issue. Also with an App Check debug token the whole thing just works. I don't understand why it breaks firebase connection even if i haven't enabled enforcement.
I appreciate any tips on how to solve this.
I found the problem i accidently left self.FIREBASE_APPCHECK_DEBUG_TOKEN = true; in my production code.
Remove this line or only use when in development envirement solved the whole problem
if (location.hostname === "localhost") {
self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}
I didn't expect this line to impact browsers where i haven't registered a debug token to fail with the regular appCheck but of course it doesn't make sense to use it in production anyways.
Based on your question, you want to enable Firebase App Check for Firestore with Web Application. Currently, Firestore App Check does not support it.
As per this Documentation:
Cloud Firestore support is currently available only for Android and iOS clients. If your project has a web app, don't enable Cloud Firestore enforcement until web client support is available.
I'll leave an answer for people who have problems with the setup. For the more detailed answer, you can check this question.
In short, you need to do a few steps.
Set up a firebase.
Go to how to set up app check.
Register reCAPTCHA here and add the secret_key to the Firebase console, and the site_key to your web code.
Initialize the Firebase in your code based on how to set up app check docs.
Get your debug_key and add it to your firebase console and code.
Finally, the code should look something like this:
Don't forget to save your key to a .env file or firebase.js or somewhere where you won't upload it to git.
if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
window.FIREBASE_APPCHECK_DEBUG_TOKEN = 'your_debug_token';
}
const appCheck = initializeAppCheck(app, {
provider: new ReCaptchaV3Provider('your_site_key_from_register'),
// Optional argument. If true, the SDK automatically refreshes App Check
// tokens as needed.
isTokenAutoRefreshEnabled: true
});

Firebase Emulator request to Google Cloud Speech to Text Api denied

I would like to test the Google Cloud Speech-to-Text API from within Firebase Emulators. I currently have a trigger set on Firebase Storage that automatically gets fired when I upload a file via the Emulator Storage UI. This makes a request to the Speech to Text API, but I keep getting a permission denied error, as follows:
Error: 7 PERMISSION_DENIED: Cloud Speech-to-Text API has not been used in project 563584335869 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/speech.googleapis.com/overview?project=563584335869 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
I understand that project 563584335869 is the Firebase Cli project.
I have set the following environment variables when starting the emulator:
export GCLOUD_PROJECT=my-actual-glcloud-project-id && export FIREBASE_AUTH_EMULATOR_HOST='localhost:9099' && export GOOGLE_APPLICATION_CREDENTIALS=./path/to/service-account.json &&
firebase emulators:start
The service_account.json key file is associated with a service_account that has the following roles, as demonstrated by running
gcloud projects get-iam-policy my_project_id --flatten="bindings[].members" --format='table(bindings.role)' --filter="bindings.members:serviceAccount:my_service_account#my_project_id.iam.gserviceaccount.com"
ROLE
roles/speech.admin
roles/storage.admin
roles/storage.objectAdmin
roles/storage.objectCreator
roles/storage.objectViewer
Since the credentials for the service account I am using should have admin access to the speech to text api, why do I keep getting a permission denied error when running from the emulator, and how can I fix it?
The project id 563584335869 is not yours. It is firebase-cli cloud project’s project-id. In this case, the problem is arising because you have to set your own configuration using your credentials or your key.
You can see below a code for NodeJS which I found in github[1] where it shows how to configure your authentication to use the API.
// Imports the Google Cloud client library
const textToSpeech = require('#google-cloud/text-to-speech');
// Create the auth config
const config = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
// Creates a client
const client = new textToSpeech.TextToSpeechClient(config);
[1]https://github.com/googleapis/nodejs-text-to-speech/issues/26
EDIT
There are different ways to set up your authentication for speech to text. One way to resolve this problem would be to add the same auth configuration as the Text-to-Speech and it should look something like this in your code.
// Imports the Google Cloud client library
const speech = require('#google-cloud/speech');
// Create the auth config
const authconfig = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
// Creates a client
const client = new speech.SpeechClient(authconfig);
Another way to solve this problem according to this Google Cloud Documentation[2] is to setup your authentication.
[2]https://cloud.google.com/speech-to-text/docs/libraries#setting_up_authentication

Firebase functions authorize only requests from Firebase hosting app

I have a simple Firebase Hosting web application (based on a Vue app) which invokes Firebase Function (Google cloud function):
import firebase from "firebase/app";
import "firebase/functions";
firebase.initializeApp(firebaseConfig);
let functions = firebase.app().functions("us-west4");
let testFunction = functions.httpsCallable("testFunction");
and corresponding functions index.js file:
const functions = require("firebase-functions");
exports.testFunction = functions.region("us-west4").https.onCall(async (data, context) => {
console.log("Very important things here");
return {"response": "data"};
});
From security perspective is it possible to
Allow this invocation only from my domain name (Firebase hosting) myhostedapp.web.app
Check for any kind of authentication (e.g. token) that my JS app provides during the request?
I've tried accessing context.auth property (see docs) buth seems like some kind of service account is required and this cannot be used when called from Firebase hosting web application.
Basically I don't want my function to be publicly accessible (simple invocation via trigger url), so any advice or best practice for securing Firebase Hosting + Functions would be appreciated.
Firebase just released a new feature called App Check that does precisely this: it allows the Cloud Functions in your project to only be invoked from apps that are registered in that project.
For web apps this happens through reCAPTCHA v3, which . Then once you enable enforcement of the check on Cloud Functions, it will reject any requests coming from other sources.
You'll typically want to combine App Check with your current user-based approach, so that you can easily block calls from outside your web app, but also still ensure authenticated users only can make calls that they're authorized for.

No auth information for emulated local Cloud function

I have the following setup: Vue Webapp with Hosting, Cloud Functions and Firestore.
When deploying the app to the Google Cloud, everything works. When i emulate the functions and hosting using firebase emulators:start --only functions,hosting, I can use the hosted app and cloud functions, but the authentication information from context.auth is undefined.
This works in the cloud but not in the emulator. Any ideas or solutions?
Note: I also set the admin credentials as described in the Docs.
Edit: I have a colleague that runs the emulator on windows successfully including authentication info, but I can't find any differences in the setup?!
export default class AuthGuard {
private readonly authentication: object;
public constructor(context: any) {
this.authentication = context.auth;
}
public isAuthenticated(): boolean {
console.log('this.authentication', this.authentication ); // this in undefined
if (this.authentication === undefined) {
throw new functions.https.HttpsError(
'unauthenticated',
'The request requires user authentication',
);
}
return true;
}
[Firebaser here] This is a bug in the emulators and it was fixed in version 7.16.2:
https://github.com/firebase/firebase-tools/releases/tag/v7.16.2
To update, just re-install the Firebase CLI at the latest version.
As mentioned in the official documentation Set up admin credentials in Emulator, this is an optional setting that you need to configure, in case you want to test your authentication as well.
To set up the authentication, please, follow the below steps.
Open the Service Accounts pane of the Google Cloud Console.
Make sure that App Engine default service account is selected, and use the options menu at right to select Create key.
When prompted, select JSON for the key type, and click Create.
Set your Google default credentials to point to the downloaded key:
As per the documentation indicates, this should be good for testing, since now it will be using the Admin SDK for tests. One of the examples mentioned is calling the function admin.auth().getUserByEmail(email).
Besides that, in this other below case - now from a Github issue - you can get more examples and information on how to use authentication in local emulator.
Add firebase authentication emulator to emulator suite
I would like to add as well, that as per the official documentation indicates, the Local Emulator is in Beta. So, it might be worth it to contact the Firebase support team directly, via their free support.
Let me know if the information helped you!

How to get the firebase functions URL in a firebase hosted webapp?

I am working on a project in which we have pre-existing cloud functions in use with Firebase. We are adding a small React SPA using firebase cloud hosting, and this SPA will interact with some of the existing public cloud functions.
The way we have been doing things so far, we have a dev project, and a production project in Firebase. For cloud functions, this works fine, we have environment specific config set up with firebase functions:config:set for differentiations between prod and dev servers.
The problem comes with the hosted SPA contacting the cloud functions. I've seen a lot of questions on how to access the environment config in the hosted code, eg this one: How to reference Firebase Functions config variables from a Firebase-hosted application? where the answer seems to be to have firebase functions that return the values of the environment variables, but for me this just moves the problem further back on step. I fully understand that having the environment variables accessible to this code would be a massive security problem as the SPA is run in the browser.
The only environment specific config I really need for the hosted SPA is the base address for the cloud functions.
eg if in my cloud functions I have
const functions = require('firebase-functions');
const express = require('express');
const test = express();
test.on('/hello/:target', (req, res) => {
res.send(`Hello ${req.params.target}`);
})
exports.test = functions.https.onRequest(test);
then having deployed, this cloud function is available both at https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net/test/hello/world and https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net/test/hello/world . How would I best get the appropriate root url (https://us-central1-DEV-PROJECT-NAME.cloudfunctions.net or https://us-central1-PROD-PROJECT-NAME.cloudfunctions.net) for the project that the SPA is deployed to?
eg. is there some global I can access in the frontend js code where I could do something like:
const url = `${__FIREBASE_GLOBALS__.cloudFunctions.baseUrl}/test/hello/${input}`;
And have the url be correctly defined based on which project the hosted app is deployed to?
I'm assuming here that you're not using Firebase in any other way in your SPA other than to call Cloud Functions (since you didn't say otherwise).
Read the Firebase web setup docs for Firebase Hosting, especially the section on SDK imports and implicit initialization. When you host a site with Firebase Hosting, there are some special URLs that give you the configurations for that project. There are some special script includes that give you access to Firebase products. In particular, note the relative path URI /__/firebase/init.js will yield JavaScript that initializes the Firebase JavaScript SDK with the default settings for your project. Go ahead and access that in a browser pointing to your web app. You're probably interested in the projectId property of the config.
If you want to get a hold of that value, you can use the Firebase SDK, which would be initialized by the script includes from the first link above. Minimally, you could add:
<script src="/__/firebase/5.8.2/firebase-app.js"></script>
<script src="/__/firebase/init.js"></script>
Then later on (see API docs):
firebase.app().options.projectId
to get the ID of the project where Firebase Hosting is serving the content. You can use that to build the URL to your functions.
It might also be convenient for you to port your HTTP functions to callable functions and invoke them from the web site with the Firebase SDK to invoke kthem. Or not.
I was able to get the region and appId from the environment variables.
eg:
console.log(process.env);
Check your firebase logs
{ ...
ENTRY_POINT: 'server',
X_GOOGLE_FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
FIREBASE_CONFIG: '{"projectId":"pid","databaseURL":"https://pid.firebaseio.com","storageBucket":"pid.appspot.com","locationId":"europe-west"}',
X_GOOGLE_FUNCTION_NAME: 'server',
FUNCTION_TRIGGER_TYPE: 'HTTP_TRIGGER',
X_GOOGLE_GCLOUD_PROJECT: 'pid',
FUNCTION_NAME: 'server',
X_GOOGLE_GCP_PROJECT: 'pid',
X_GOOGLE_FUNCTION_REGION: 'us-central1',
FUNCTION_REGION: 'us-central1',
X_GOOGLE_ENTRY_POINT: 'server',
GCLOUD_PROJECT: 'pid',
GCP_PROJECT: 'pid',
... ommited
}
Out of these GCP_PROJECT, GCLOUD_PROJECT, FUNCTION_REGION, FUNCTION_NAME should work. So for eg. process.env.FUNCTION_REGION
Not sure how reliable this will be.

Resources