firebase API add analytics through service account 403 permission error - firebase

I have a service account that has successfully made calls to both firebase API and google analytics v4 admin API.
It can create analytics v4 properties through the analytics admin API, but from firebase for some reason it fails.
The reason I'm doing this through firebase is that I need to add app data streams, and that's not possible through the analytics admin API (gotta love google...)
Making the call below as in the docs fails with:
// the response
code: 403,
errors: [
{
message: 'The caller does not have permission',
domain: 'global',
reason: 'forbidden'
}
]
// the call
apiClient.request({
method: 'POST',
url: `https://firebase.googleapis.com/v1beta1/projects/${projectNumber}:addGoogleAnalytics`,
data: JSON.stringify({
analyticsAccountId,
}),
}),

403 errors appears when the email account you are using doesn't have enough permission to process the action you're doing. You must be an Owner for the existing Firebase Project and have the Edit permission for the Google Analytics account, to be able to add Analytics in your project using the API.

Related

How to use a Google Doc Apps Script OAuth token to do Firebase IDP login? [duplicate]

For a while ago I was using integration of Firebase in Google Apps Script as a server side and it was working finely and still working in my old projects.
But today after creating a new Firebase project and a new realtime database then trying to integrate Firebase Project into my Google Script project I got an error and it's not working completely. And I realize that Firebase deprecated database secret for new projects.
So, my question now is how to come over this problem? Is there another way to integrate Firebase into Google Script project?
You'll need to add the correct OAuth scopes to the manifest file of your Apps Script project, and then pass in an access_token parameter (or in the Authorization header) instead of the auth parameter you currently use.
Based on Doug's gist here, the basic steps are:
Open the manifest.json from the script editor, by clicking View > Show manifest file.
Add or edit the manifest to have these OAuth scopes:
"oauthScopes": [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/spreadsheets"
]
That last scope grants it access to the spreadsheet itself. If you're using another GSuite type (document, slides, form, etc) you'll need the scope that corresponds that to type.
Now you can get the OAuth token from within your script and add it to your request:
var response = UrlFetchApp.fetch(databaseUrl, {
method: "PUT",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer "+ScriptApp.getOAuthToken()
},
payload: JSON.stringify(json)
});
Logger.log(response.getContentText());
A major advantage of this is that your script will now run as an actual user, meaning that you can ensure it can only perform authorized actions through security rules.

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.

Google Calendar Push Notifications to Firebase Function

I'm a little stuck here.
I'm trying to hook a Calendar Push Notifications (https://developers.google.com/calendar/v3/push) to a Firebase HTTPS Function.
Pratically, I use a FF to create a calendar and I want to watch it for changes.
I authorized my domain and compose a request from the first function using the appspot service account ('#appspot.gserviceaccount.com') with the scope 'https://www.googleapis.com/auth/calendar' but I always received the error message: "Unauthorized WebHook callback channel: https://******.cloudfunctions.net/calendarWebHook"}],"
I'm trying to understand if the service account doesn't have the authorization to create the watcher or the webHook or something else.

How to integrate Firebase into Google Apps Script without using (deprecated) database secret

For a while ago I was using integration of Firebase in Google Apps Script as a server side and it was working finely and still working in my old projects.
But today after creating a new Firebase project and a new realtime database then trying to integrate Firebase Project into my Google Script project I got an error and it's not working completely. And I realize that Firebase deprecated database secret for new projects.
So, my question now is how to come over this problem? Is there another way to integrate Firebase into Google Script project?
You'll need to add the correct OAuth scopes to the manifest file of your Apps Script project, and then pass in an access_token parameter (or in the Authorization header) instead of the auth parameter you currently use.
Based on Doug's gist here, the basic steps are:
Open the manifest.json from the script editor, by clicking View > Show manifest file.
Add or edit the manifest to have these OAuth scopes:
"oauthScopes": [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/script.external_request",
"https://www.googleapis.com/auth/spreadsheets"
]
That last scope grants it access to the spreadsheet itself. If you're using another GSuite type (document, slides, form, etc) you'll need the scope that corresponds that to type.
Now you can get the OAuth token from within your script and add it to your request:
var response = UrlFetchApp.fetch(databaseUrl, {
method: "PUT",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer "+ScriptApp.getOAuthToken()
},
payload: JSON.stringify(json)
});
Logger.log(response.getContentText());
A major advantage of this is that your script will now run as an actual user, meaning that you can ensure it can only perform authorized actions through security rules.

Http request not working in Firebase webhook for google assistant

I have created the simple firebase function for Google assistant but when I try to send HTTP request I got an error in google actions simulator
MalformedResponse
'final_response' must be set.
The next action works properly:
app.intent('Default Welcome Intent', (conv) => {
conv.ask(`What do you want?`);
});
But the next is not working (produce the previous error):
app.intent('turnOff tv', (conv) => {
request('http://someurl.com', (res) => {
conv.ask('Alright, your value is');
});
});
I was installing request module before (npm install request --save).
And I'm using the free firebase account.
How can I make an HTTP request in Firebase function while triggering a Google action from Google home?
Thanks!
Sadly, using the free plan for Google Functions, you cannot trigger a request for an external service other than Google.
The firebase free plan allows outbound networking to Google Services only.
Source: Firebase pricing for details.

Resources