Hosting Vector Tiles From Firebase Cloud Storage - firebase

I am hosting vector tiles that are being consumed by my javascript web application with mapboxGL.
MapboxGL requests these tile from the bucket in a very specific way. When instantiating the map you provide a url to your bucket like this:
addLayer({
source: {
type: "vector",
tiles: [
`https://storage.googleapis.com/${bucketName}/{z}/{x}/{y}.pbf`,
],
minZoom: 4,
maxZoom: 12,
}
})
And the map will request the proper tiles based on the viewport. You can transform the request and add Authorization: Bearer {{tokens}} to each request for a tile by adding the following property:
transformRequest: (url, resourceType) => {
if (url.startsWith(settings.VECTOR_TILE_BUCKET_URL)) {
return {
url: url,
headers: {
Authorization: `Bearer ${accessToken}`,
'Cache-Control': 'no-cache'
}
};
}
}
Now here is my question for someone familiar with firebase. When setting 'rules' on my cloud storage bucket, can I allow read access for all users in the account and then pass the firebase token that the user receives as a Bearer {{token}} in these tile requests? Is there another method I could protect my vector tiles with firebase's services that I'm not thinking of? I read the documentation where it shows how to access cloud storage bucket using the firebase SDK but it's unclear if I can also auth into their bucket by passing the token in a more traditional format as a query param.

In the Official Cloud Storage Authentication Documentation, you can see many methods of authentication (with gsutil, Client Library, Api), but as you are using Firebase, Firebase SDK comes with Firebase Authentication built-in, so Firebase SDK is the only way to access the bucket, such as the SDK handles all auth related operations and using other methods could expose your apps to risks.
An another way to authenticate could be using Real-time Database Rest API, which I think will not help you for your scenario.

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.

Firebase Storage REST call: How to set auth.uid manually and make Strorage Rules apply

Currently, I am using a Firebase Service Account to access Firebase Storage via REST calls.
However, Storage is based on Security Rules which do not apply to the Service Account, which seems to have super powers.
Is there a way to get rid of the Service Account and use a proper user-id instead, thus making the Storage Security Rules work properly?
Background
I am using Flutter-desktop (windows) and there is no library available to access Firebase Storage properly. Thus, I have to use REST calls.
Firebase Auth however is working properly via the flutter_auth_desktop plugin which provides an id-token.
FirebaseAuth.instance.currentUser!
.getIdToken()
.then((token) => token);
But I have no idea how to incorporate that into the REST calls, these approached do not work:
...(url)...&auth=$accessToken
or
headers: {'Authorization': "$accessToken"}
or
headers: {'Authorization': "Bearer $accessToken"}

Firebase - RealTime Database - Rules and Auth

I'm designing a web application to be able to conduct online questionnaires.
I've deployed the Flutter web app to Firebase hosting and the app's database lies in the Firebase real-time DB.
Since anyone can submit a response, I really don't want to set up an OAuth-based authentication system.
However, I also do NOT want my APIs to be open to everyone as this poses a security risk.
This is how my request headers look (as of now) -
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
};
This is how the realtime db rules look like -
{
"rules": {
".read": true,
".write": true,
}
}
Now, the question is, how can I make sure that my APIs are not exposed to everyone while I still can use them inside my application (which works without any user auth)?
Also, I want to make sure that Firebase DB at least checks that a request is coming from within my deployed web app and only then it lets it use the APIs. If someone is going to those APIs directly (from outside the app, in the browser, they simply should not work).
Is there any way I can achieve this ?? Probably by sending something inside those headers and then checking the same on the server before the data is sent back to the client.
Your help would be highly appreciated!

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.

Using firebase google auth and google fit api with single OAuth2 token

I am currently working on a simple web app that will make an GET call to a user's google fit data.
To reduce the complexity of the app, I plan to host it on firebase and take full advantage of cloud functions, firestore and the authentication tools.
So far, I have managed to read my google fit data using googleapis node library on the frontend by passing the fitness scope https://www.googleapis.com/auth/fitness.activity.read
as part of the google sign in button.
If I were to use firebase's google auth implementation, this would have a different client ID / different scopes than the credentials I made for reading the google fit data.
It seems if I want to use firebase AND google fit, the user would have to login using firebase's google auth so I can authenticate database writes to my app, and also grant me access to their google fit data from within the app.
If there a way this could be combined so I could use a single token to authenticate and read google fit data?
Looks like I just had to dig a bit deeper, you can add scopes to your google auth provider instance.
https://firebase.google.com/docs/auth/web/google-signin
var provider = new firebase.auth.GoogleAuthProvider();
provider.addScope('https://www.googleapis.com/auth/fitness.activity.read');
firebase.auth().signInWithPopup(provider).then(function(result) {
var token = result.credential.accessToken;
var user = result.user;
})
Then to make the fitness reading, use the access token returned in the Authorization header
Authorization: `Bearer ${accessToken}`
axios.post('https://www.googleapis.com/fitness/v1/users/me/dataset:aggregate?alt=json', data, axiosconfig)
Note: If you want to do background requests to a users data while they are not in the app (offline in oauth2 terms), you will need to use firebase in conjunction with gapis js library.

Resources