How do you apply security rules to Firepad? - firebase

I'd like to secure a Firepad. After my research, I discovered https://github.com/firebase/firepad/tree/master/examples/security, which lists some rules in .json for securing a Firepad. I have looked extensively at the Firepad docs, but cannot figure out how to apply the example security rules when initializing a Firepad.
How do you initialize a Firepad using the example security rules embedded in JSON (referred to above)?
EDIT 01:
I can see that Firebase lets you configure the rules, I'm interested in finding out how to APPLY these rules. For example, if I initialize a Firepad like this:
var firepadRef = new Firebase('MYURL');
var codeMirror = CodeMirror(document.getElementById('firepad'), { lineWrapping: true });
var firepad = Firepad.fromCodeMirror(firepadRef, codeMirror,
{ richTextShortcuts: true, richTextToolbar: true, defaultText: 'Hello, World!' });
how do I incorporate the rules in JSON? As an option to the Firepad.fromCodeMirror() call? As some sort of parameter to the new Firebase() call?

If you haven't already, you'll probably want to learn a bit about Firebase, the backend for Firepad. As part of setting up Firepad, you'll create a Firebase account and a Firebase database. The Firebase database lets you configure Security Rules by entering them in the "Security & Rules" tab of your the dashboard (or uploading them via the REST API).
To be clear, the security rules are not set via the code that initializes Firepad, since that is untrusted code running in the browser and so any user of your app could modify it.

Related

Custom logging from firebase function

I'm trying to follow this guide to put some custom logging into a firebase function. The function itself is running, and I can see the data being passed in (it's an https 'callable' function). But as soon as it hits the line where it tries to actually write that log entry, I get "Error: 7 PERMISSION_DENIED"
Since the console.log() calls write to the cloud logs, I'd assumed the firebase function has access to Cloud Logging. But perhaps it needs additional permission? I can't find any reference to where this should be set on that page though.
// Logging, logName, region, functions are provided by the surrounding app
const logging = new Logging()
const log = logging.log(logName)
const METADATA = {
resource: {
type: 'cloud_function',
labels: {
function_name: 'CustomLog',
region
}
}
};
exports = module.exports = functions.https.onCall(async data => {
const exVersion = 6
const exId = data.exId
console.log('***** exVersion:', exVersion, 'exId:', exId) // exId from caller
const entry = log.entry(METADATA, data.error) // data.error from caller
console.log('METADATA:', METADATA) // Shows in Logs Explorer
console.log('entry:', entry) // Shows in Logs Explorer
log.write(entry) // Results in Error: 7 PERMISSION_DENIED
return {
exVersion,
exId,
}
})
If I run it from the CLI using firebase function:shell, the log entry is created correctly, so I'm pretty confident the code is correct.
OK, I finally tracked it down. According to this answer, the service account used by firebase functions is {project-id}#appspot.gserviceaccount.com, and in my project, that account did not have the 'Logs Writer' role. Adding that role solves the problem.
I find it odd that the firebase functions don't need that role to log messages using console.log(), but perhaps that call is intercepted by the functions environment, and the logs are written as a different service account. It also explains why the functions running locally were able to write the logs, as they run using the 'owner' service account, which has full access.
According to the Firebase documentation page you have linked:
The recommended solution for logging from a function is to use the
logger SDK. You can instead use standard JavaScript logging calls such
as console.log and console.error, but you first need to require a
special module to patch the standard methods to work correctly:
require("firebase-functions/lib/logger/compat");
Once you have required the logger compatibility module, you can use console.log() methods as normal in your code.
Thus you might to require this library, however I am not sure this is producing your "Error: 7 PERMISSION_DENIED error, but you might also try some solutions that have worked for some members of the community.
Perhaps the logging API is not enabled in your project. You'll get a permission denied error when attempting to use it in that case.
It's a couple levels in, but the guide you linked points to
https://github.com/googleapis/nodejs-logging#before-you-begin, which includes a step to "Enable the Cloud Logging API"

Firebase 3rd-party AuthProvider (Google/Facebook/etc) login with chrome extension manifest v3

Manifest version 3 for Chrome extensions have been killing me lately. Been able to navigate around it so far, but this one has really stumped me. I'm trying to use Firebase authentication for a Chrome extension, specifically with 3rd party auth providers such as Google and Facebook. I've setup the Firebase configuration for Login with Google and created a login section in the options page of the Chrome extension and setup the Firebase SDK.
Now, there are two login options when using an auth provider, signInWithRedirect and signInWithPopup. I've tried both of these and both have failed for different reasons. signInWithRedirect seems like a complete dead end as it redirects to the auth provider, and when it attempts to redirect back to the chrome-extension://.../options.html page, it just redirects to "about:blank#blocked" instead.
When attempting to use signInWithPopup, I instead get
Refused to load the script 'https://apis.google.com/js/api.js?onload=__iframefcb776751' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.
In v2, you could simply add https://apis.google.com to the content_security_policy in the manifest. But in v3, the docs say
"In addition, MV3 disallows certain CSP modifications for extension_pages that were permitted in MV2. The script-src, object-src, and worker-src directives may only have the following values:"
self
none
Any localhost source, (http://localhost, http://127.0.0.1, or any port on those domains)
So is there seriously no way for a Google Chrome extension to authenticate with a Google auth provider through Google's Firebase? The only workaround I can think of is to create some hosted site that does the authentication, have the Chrome extension inject a content script, and have the hosted site pass the auth details back to the Chrome extension through an event or something. Seems like a huge hack though and possibly subject to security flaws. Anyone else have ideas??
Although it was mentioned in the comments that this works with the Google auth provider using chrome.identity sadly there was no code example so I had to figure out myself how to do it.
Here is how I did it following this tutorial:
(It also mentions a solution for non-Google auth providers that I didn't try)
Identity Permission
First you need permission to use the chrome identity API. You get it by adding this to your manifest.json:
{
...
"permissions": [
"identity"
],
...
}
Consistent Application ID
You need your application ID consistent during development to use the OAuth process. To accomplish that, you need to copy the key in an installed version of your manifest.json.
To get a suitable key value, first install your extension from a .crx file (you may need to upload your extension or package it manually). Then, in your user data directory (on macOS it is ~/Library/Application\ Support/Google/Chrome), look in the file Default/Extensions/EXTENSION_ID/EXTENSION_VERSION/manifest.json. You will see the key value filled in there.
{
...
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAgFbIrnF3oWbqomZh8CHzkTE9MxD/4tVmCTJ3JYSzYhtVnX7tVAbXZRRPuYLavIFaS15tojlRNRhfOdvyTXew+RaSJjOIzdo30byBU3C4mJAtRtSjb+U9fAsJxStVpXvdQrYNNFCCx/85T6oJX3qDsYexFCs/9doGqzhCc5RvN+W4jbQlfz7n+TiT8TtPBKrQWGLYjbEdNpPnvnorJBMys/yob82cglpqbWI36sTSGwQxjgQbp3b4mnQ2R0gzOcY41cMOw8JqSl6aXdYfHBTLxCy+gz9RCQYNUhDewxE1DeoEgAh21956oKJ8Sn7FacyMyNcnWvNhlMzPtr/0RUK7nQIDAQAB",
...
}
Copy this line to your source manifest.json.
Register your Extension with Google Cloud APIs
You need to register your app in the Google APIs Console to get the client ID:
Search for the API you what to use and make sure it is activated in your project. In my case Cloud Firestore API.
Go to the API Access navigation menu item and click on the Create an OAuth 2.0 client ID... blue button.
Select Chrome Application and enter your application ID (same ID displayed in the extensions management page).
Put this client ID in your manifest.json. You only need the userinfo.email scope.
{
...
"oauth2": {
"client_id": "171239695530-3mbapmkhai2m0qjb2jgjp097c7jmmhc3.apps.googleusercontent.com",
"scopes": [
"https://www.googleapis.com/auth/userinfo.email"
]
}
...
}
Get and Use the Google Auth Token
chrome.identity.getAuthToken({ 'interactive': true }, function(token) {
// console.log("token: " + token);
let credential = firebase.auth.GoogleAuthProvider.credential(null, token);
firebase.auth().signInWithCredential(credential)
.then((result) => {
// console.log("Login successful!");
DoWhatYouWantWithTheUserObject(result.user);
})
.catch((error) => {
console.error(error);
});
});
Have fun with your Firebase Service...

Write Cloud Firestore security rules

I'm creating a web application using the Firebase Cloud Firestore, and I would like to write the security rules I've imagined, but I can't find any better documentation on this subject, it's always simple things, like check if the user is signed in.
So what I want to do is to check if the article that the client wants to read has a property called public, set to true. And maybe I can check the source of the request, to be sure it comes from my website's url ? I would like to find a solution to allow read without needing the user to sign-in , but also with a minimum of security.
And is it possible to return true if the property is undefined ? I would like to set the article public by default, but i don't know how to do it.
As Frank said, you cannot restrict access to Firestore from a specific domain. However and because you use some api key to call your firebase resources, you can restrict the use of this key to specific domain. You can do this by going to the GCP credentials page --> the API key you want to restrict. From there you can retrict how this key is used to websites, apps ...etc.
For you other question about checking if artice has public property, that can be done easily by use of security rules like:
service cloud.firestore {
match /databases/{database}/documents {
match /articles/{articleId} {
// I chose visibility as a prop here but it can be anything
allow read: if resource.data.visibility == "public"
}
}
}
To set the article as public by default you can do that from your client app when you create the article document itself by setting a property let's say "visibility" to public. something like this in your app:
// Add a new document with a generated id.
db.collection("articles").add({
visibility: "public"
...
})
I'm not sure if you can whitelist only your domain, but you can do pretty much everything you need with security rules
So what I want to do is to check if the article that the client wants to read has a property called public, set to true
allow read: if resource.data.yourPropertyName == "public"
I would like to find a solution to allow read without needing the user to sign-in
allow read: if true;
Keep in mind that those are not supposed to be used as filters, they are supposed to control who can write/read stuff

Firebase security - user and admin permission

I've recently built an app using Firebase as the data store and secured it using the security rules that only the user can read and edit their data which all works fine.
But now I want to build an admin section to list users and update details if need be, but the problem I'm running into is the fact that I cant access their data as I'm not the user. I'm seeing if its possible to allow read or write permissions to the user or admin?
UPDATE
Token generation
var tokenGenerator = new FirebaseTokenGenerator(authSecret);
var token = tokenGenerator.createToken({admin: true});
Security rule
".read": "auth.admin == true || otherauthmthod"
The method that you described above will work, assuming you update your security rules to look for the auth.admin bit. Alternatively, and likely a bit easier, is to generate an admin token, which will allow you to skip the execution of security rules entirely. This can be accomplished via:
var token = tokenGenerator.createToken({ any: "data" }, { admin: true });
See https://github.com/firebase/firebase-token-generator-node for more details.

Inner auth() seems to adhere to outer auth() rules

In our application we use Firebase's custom login functionality to store some metadata in user's auth token.
Later we send this token to one of our web applications to perform a task on behalf of the user using a new admin token to disable security rules. The idea is that a particular location is not writable by authenticated users directly, but data could be written in that location after some server side calculations and validations are done.
Here's a sample code of what I'm trying to do:
var testRef = new Firebase(firebaseApp + 'test');
testRef.auth(userFirebaseAuthToken, function(error, result) {
if (!error) {
var userId = result.auth.userId;
// perform validations and calculations
var tokenGenerator = new FirebaseTokenGenerator(firebaseSecret);
var token = tokenGenerator.createToken({admin: true});
var protectedRef = new Firebase(firebaseApp + '/protected/');
protectedRef.auth(token, function(error) {
if (!error) {
protectedRef.child('foo').push({id: userId});
}
});
}
});
But I get this error:
FIREBASE WARNING: set at /protected/foo/-Ityb1F6_G9ZrGCvMtX- failed: permission_denied
When desired behavior is to be able to write in that location using an admin token.
I understand that this might not be a Firebase issue, but some JavaScript good/bad parts, but what I need to do is to write in some protected location on behalf of a user which even though is not authorized to write in that location, but needs to be authenticated.
Based on what I've seen from my test units and from experience, I don't think that new Firebase actually gives you an independent connection to the data. That is to say, these are both connected to the same Firebase instance internally (I think):
var refA = new Firebase('...');
var refB = new Firebase('...');
So if you want to re-auth, I'm pretty sure you need to call unauth first, which will probably affect your testRef instance as well.
If you truly need to have multiple instances opened to the database with different auth at the same time, then you'll have to look at node-fibers or some other worker pool model, which will allow separate connections.
However, give some thought to this; you are probably overthinking your approach. If you are writing on behalf of a user who doesn't have permissions, then you probably don't actually need to be authenticated as that user.
I've written an entire app with secure Firebase components that are consumed by a third-party app, and then written back to privileged paths and then read by users, and haven't yet run into a condition where the server would need to demote its permissions to do this.
That's not meant to presume I know your use case, just to give you some encouragement to keep things simple, because trying to juggle authentication will not be simple.
My approach is to treat the Firebase security rules as a last defense--like my firewall--rather than part of the programming algorithm used by privileged processes.

Resources