Firebase Emulator request to Google Cloud Speech to Text Api denied - firebase

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

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
});

Accessing firestore from self-hosted server

I need to be able to access (read, write) firestore from my self-hosted server (not on gcloud).
I also need to reserve write permissions to the app hosted on my self-hosted server, and allow noone else to be able to write to the server, so the security rules for write applications are denied by default.
According to this document:
https://firebase.google.com/docs/firestore/client/libraries#server_client_libraries
using a service account should allow me to be able to access all firebase products, including firestore, without any restrictions.
But, as soon as I restrict the security rules, I am unable to write to firestore from my service, instantiated with service account credentials following the steps in this documentation:
https://firebase.google.com/docs/admin/setup#initialize-sdk
I get the following error:
PERMISSION_DENIED: Missing or insufficient permissions.
What do I have to do to allow my application to write to firestore from my own servers?
Update:
I am using java for my backend applications.
Servers (or cloud functions) are secure environment which can be accessed only by you and people you have authorized. You don't actually use the Firebase Client SDKs in a server. Instead you use the Firebase Admin SDK. It uses a service account and has privileged access to your Firebase Project.
What does privileged access mean?
The documentation has explained it all but long story short it has complete access like you to the project. It does not follow any security rules.
If you are implementing the Admin SDK which will deal with API requests coming from your clients then make sure you authenticate them. To do so you would ideally pass the Firebase ID Token from your frontend to the API and verify
it using the Admin SDK which returns a decoded token object containing UID, custom claims and some metadata about that user.
I'm not sure which language you use but here's a simple getting started for NodeJS:
// Install the Admin SDK
npm install firebase-admin
// index.js
const admin = require("firebase-admin")
const path = require("path");
admin.initializeApp({
credential: admin.credential.cert("path/to/key")),
databaseURL: "https://<project-id>.firebaseio.com/"
})
const firestore = admin.firestore()
const auth = admin.auth()
// firebase.firestore() and firebase.auth() in client SDKs
Rest of the syntax is mostly the same as Client SDK.
You can create a service account from the project settings.
The issue that I was facing has already been reported here:
https://github.com/googleapis/java-firestore/issues/431
The fix was an odd one, and cannot be explained without going deep into the SDK and how it works, which I did not have time to check:

CLOUD_SDK_CREDENTIALS_WARNING We recommend that most server applications use service accounts instead

Context: I have just learn a trick to get (download) data from FireStore Dashboard. Obviouslly, it is much easier just open Google Dashboard on Browser and see with my eyes to own Google Dasboard. Nevertheless, for personal reasons, in my company the operators can't look at a third Dashboard. They only can see internal Dashboards. I am trying some workaround where I can get/download the same data used for fill in Dashboard and imported it to our internal solution based on Dynatrace/ELK.
For learning purposes, in order to download Google Dashboard data I followed:
1 - Get a ACCESS_TOKEN using gcloud
C:\Program Files (x86)\Google\Cloud SDK>gcloud auth application-default print-access-token
C:\Program Files (x86)\Google\Cloud SDK\google-cloud-sdk\bin\..\lib\third_party\google\auth\_default.py:69: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
ya29. ... ACCESS-TOKEN ...7hu
2 - using the above ACCESS_TOKEN to get Dashboard data like:
curl --location --request GET 'https://monitoring.googleapis.com/v3/projects/firetestjimis/timeSeries?filter=metric.type%20%3D%20%22firestore.googleapis.com%2Fdocument%2Fread_count%22&interval.endTime=2020-05-07T15:01:23.045123456Z&interval.startTime=2020-05-05T15:01:23.045123456Z' --header 'Authorization: Bearer ya29...ACCESS-TOKEN 7hu'
Obviously this is just an example how to get how many conections satisfied the filter criteria. I can keep searching adjusting the API and filters according to Google Cloud Metrics and Google Cloud API v3
Other example of getting Dashboard metada this time from API version 1 is
curl --location --request GET 'https://monitoring.googleapis.com/v1/projects/firetestjimis/dashboards' --header 'Authorization: Bearer ya29... ACCESS-TOKEN ...7hu'
The warning when getting the ACCESS-TOKEN from gcloud encourage to see Authentication guidance and I did it. Well, it doens't explain how to fix this warning neither why "If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error". I can see my trick to get data from Dashboard is working but it seems I am relying on strange way to get a ACCESS-TOKEN.
So my straight question is: what is the appropriate steps to get manually an ACCESS-TOKEN and use it in curl/postman avoiding such warnning?
It seems to me that, based on this stackoverflow answer the root cause is "... This error message means you're using a User account, and not a service account..." So how can I fix it? Do I have to create a service account? If so, how? At the end of this accepted answer I read "... to use the true application default you can use gcloud auth application-default login..." And it is exactly how I am logging with gcloud: run gcloud auth application-default login, when open Google SingleSignOn I pick my email which is the the same user I registered in Firebase account. The answer also mentioned "... method to associate a specific service account is gcloud auth activate-service-account --key-file ...." I want give a try on it but which key-file is he/she talking about?
In case it is relevant, in my case I am only using FireStore under Firebase project (I am not using anything else other than FireStore).
*** EDITED after John's answer
We are moving soon this project to production.
1 - Our Mobile App will create money transfer by posting it to our internal microserve. Such post request will return a CustomToken generated from our internal NodeJs server.
2 - Our internal microservice will replicate such transfer to Firestore and update its state on Firestore accordingly.
3 - Instead of our Mobilie App poll or listen our internal microservice to get the status it will listen to Firestore for getting the status from respective document. In order to listen, it will use the CustomToken returned from post in step 1. Our company wants just take advantage of Real Time Database feature from Google Firestore for this project (reactive approach).
Do you see any consideration when compared what I am doing with your statement: "Google prefers in most cases that you authorize using a service account"?
The CustomToken is created internally with this NodeJs server and depending on uid extrated from antenticated user authentication/users from Google Firebase:
const admin = require('firebase-admin');
exports.serviceAccount = {
"type": "service_account",
"project_id": "firetestjimis",
"private_key_id": "ecfc6 ... fd05923",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIE .... 5EKvQ==\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fg6p9#firetestjimis.iam.gserviceaccount.com",
"client_id": "102422819688924138150",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fg6p9%40firetestjimis.iam.gserviceaccount.com"
}
admin.initializeApp({
credential: admin.credential.cert(exports.serviceAccount)
});
var uid = "NS .... Ro2"; //copied from https://console.firebase.google.com/project/firetestjimis/authentication/users
var claim = {
control: true
};
admin.auth().createCustomToken(uid)
.then(function (customToken) {
console.log(customToken)
})
.catch(function (error) {
console.log("Error creating custom token:", error);
});
Our mobile (example in Angular but same idea for IOS and Android) has the SERVICE_ACCOUNT_JSON_FILE I downloaded like this:
environment.ts:
export const environment = {
production: false,
firebaseConfig: {
apiKey: "AIzaSy ... 3DCGihK3xs",
authDomain: "firetestjimis.firebaseapp.com",
databaseURL: "https://firetestjimis.firebaseio.com",
projectId: "firetestjimis",
storageBucket: "firetestjimis.appspot.com",
messagingSenderId: "795318872350",
appId: "1:7953 ... 32b26fb53dc810f"
}
};
app.component.ts
public transfers: Observable<any[]>;
transferCollectionRef: AngularFirestoreCollection<any>;
constructor(public auth: AngularFireAuth, public db: AngularFirestore) {
this.listenSingleTransferWithToken();
}
async listenSingleTransferWithToken() {
await this.auth.signInWithCustomToken("eyJh ### CUSTOMTOKEN GENERATED FROM INTERNAL NODEJS SERVER ABOVE ### CVg");
this.transferCollectionRef = this.db.collection<any>('transfer', ref => ref.where("id", "==", "1"));
this.transfers = this.transferCollectionRef.snapshotChanges().map(actions => {
return actions.map(action => {
const data = action.payload.doc.data();
const id = action.payload.doc.id;
return { id, ...data };
});
});
}
}
I understand that both CustomToken creation and its use from our Mobile is relying entirely on Service Account. Am I right? Did I miss some concept and I am using USER CREDENTIAL behind the scene and something that works properly in DEV environment will pop up some surprise when in production? Obviously for this question all comes from my free accoutn but in production it will be paid account but the code and steps will be exactly the same here.
There are two types of credentials used by the CLI:
User Credentials
Service Accounts
Google prefers in most cases that you authorize using a service account. However, some services require user credentials (usually non-Google Cloud Platform services). Consult the documentation for each service that you use.
Execute the following command. This will show the credentials you are using:
gcloud auth list
To configure the CLI to use a service account, execute this command:
gcloud auth activate-service-account <SERVICE_ACCOUNT_EMAIL_ADDRESS> --key-file=<SERVICE_ACCOUNT_JSON_FILE>
I wrote an article that explains in more detail (and several additional articles on services accounts, authorization, etc.):
Google Cloud – Setting up Gcloud with Service Account Credentials
So, the auth token is generated from your gcloud init authorization, which is end-user credentials. That's why you're getting that warning. Because you've used your manually signed in credentials to generate the token.
The preferred way to auth is to use a service account (documentation here: https://cloud.google.com/iam/docs/service-accounts) for authentication. That documentation will also walk you through creating a service account. If you're using it to talk to Firestore, your service account will need appropriate Firestore role permissions. Not to confuse you, but the roles in IAM are for datastore although they apply for Firestore.
This page: https://cloud.google.com/firestore/docs/security/iam lists out which roles/permissions your service account will need in order to do various things with Firestore.
Now, all that being said, the key-file it's talking about is the service account key that you can download when you create the service account. Easiest is to do it via the console in your GCP project, as when you're creating the service account, there's a handy button to create the key, and it downloads it to your local machine.

Is firebase sevices.json needed for admin SDK on cloud functions

I am trying to write a function that writes to a different database when a user writes to the default database ,
I did my research and i am a little bit confused
I saw this on firebase
var admin = require('firebase-admin');
var serviceAccount =
require('path/to/serviceAccountKey.json');
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL:
'https://<DATABASE_NAME>.firebaseio.com'
});
And also this
const app1 = firebase.initializeApp({
databaseURL: "https://testapp-1234-1.firebaseio.com"
});
const app2 = firebase.initializeApp({
databaseURL: "https://testapp-1234-2.firebaseio.com"
}, 'app2');
// Get the default database instance for an app1
var database1 = firebase.database();
// Get a database instance for app2
var database1 = firebase.database(app2);
So my question is do we need the service.json file which holds the secret key when using cloud functions admin SDK
For almost all typical use cases, you don't need to provide a service account when working with the Firebase Admin SDK in Cloud Functions. This is because Cloud Functions provides the credentials for the default service account in the project where the function is deployed. (This account can be seen as the App Engine default service account in the Cloud console.)
It's strongly recommended to initialize the Admin SDK with no arguments and accept this default account:
const admin = require('firebase-admin');
admin.initializeApp(); // accept the default service account
As part of taking this default, the Admin SDK will also understand the URL of your default Realtime Database shard, and the URL of your default Cloud Storage bucket. You won't need to provide these values. As such, your code will be much more easy to port to other projects.
If you need to access non-default resources of your project, you can also initialize other instances of firebase-admin to point to those that you explicitly configure. This is what your second code sample is illustrating. If you don't have any non-default resources to access, then don't bother with this.
If there is something that the service account can do, you can grant that role in the Cloud console. The one notable action that this account currently can't perform by default is cryptographic signing of blobs. In practical terms, this means it can't generate signed URLs for object stored in Cloud Storage. This is a fairly common thing to do in Cloud Functions. To remedy this, you need to grant the signBlob role to the default service account.

Integrate google-cloud with firebase functions

When integrating google cloud on node.js, as described here:
https://www.npmjs.com/package/google-cloud
One should include a json file with the secret credentials and keys:
var config = {
projectId: 'grape-spaceship-123',
keyFilename: '/path/to/keyfile.json'
};
When using firebase functions / google cloud functions, how can it be done?
Cloud Functions run as a trusted cloud account already. In most cases you shouldn't need to add a credentials JSON file. For example, this is how we initialize Cloud Storage in one of our apps:
const gcs = require('#google-cloud/storage')();
As you see, we're not passing in any authorization information, yet the code is still able to access Cloud Storage:
const file = gcs.bucket('YOUR_FIREBASE_PROJECT_ID.appspot.com').file(filePath);
If you have a case where you need the credentials file:
Download the JSON file with the credentials.
Put the JSON file into your `functions directory.
Refer to it from your code: keyFilename: './keyfile.json'

Resources