5

I'm using content scripts

I have created a chrome extension that will run on any number of domains where i have no control, and the extension uses Firebase login through a content-script.

My issue is that it seems like Firebase persists the state by using the indexedDB, but that makes the extension vulnerable to malicious websites stealing the user session by reading the indexedDB state (perhaps i am wrong here?).

When the extension is activated I inject a tiny react app into the DOM through a content-script which uses the Firebase javacript sdk to get data and manage login sessions. I would like for the login state of the users to be persisted so they don't have to log in every time they use the extension (otherwise i guess i could just set Firebase persistence to NONE to prevent it from using indexedDB).

Im thinking that if i can somehow save the session (refreshToken perhaps) in the chrome storage area, and then read it on load to reinitialize a user-session, then the session is more secure (not very secure, but more secure than if i use the indexedDB). Even tho the storage area is accessible by entering the right folder on the PC, its seems like a better deal to have data stored there where only the extension can access it programmatically.

Things i have tried so far

Manual way

I have tried using an endpoint with an apiKey and refresh token to get new credentials (ref: How to use the Firebase refreshToken to reauthenticate?) and this works fine. I have also been able to get the refreshToken from the user object returned after doing signInWithEmailAndPassword().

So the last piece of the puzzle is to pipe this refreshed login state into the Firebase sdk to be able to use the firebase sdk features like change listeners etc. But I have not found a way of doing this. Does anyone know if this is possible?

Built in method

There is a method called signInAndRetrieveDataWithCredential that i have tried to use. The login function returns a credential object that I have tried to pass in to signInAndRetrieveDataWithCredential to resume the user session, but the method returns an error saying the object is invalid. There is probably alot of things that should not be persisted in the credential object so this is not the preferred way, but as a last resort I could see this as an option. But as a more general question about Firebase, is there a way to keep the users logged in and store it in a place like chrome.storage?

The worst way

Persisting username/password in chrome storage is possible and would give the extension the option to sign the user in as they open it. This is of course a horrible way of going around the issue and is not really an option. But I have read that the chrome password manager also stores passwords relatively insecure, so whats the difference between that and storing password for this extension in chrome storage with perhaps some obfuscation step to prevent it from being clearly readable?

** UPDATE **

To work around this issue i have created an endpoint for validating and logging users in using custom tokens. It works as follows:

  1. User signs in, store refreshToken in chrome.storage.sync
  2. when a user starts the addon for the second time i fetch the token from the chrome storage.async
  3. post the token to a Firebase function i created that uses the method for refreshing a token described in the section "Manual way" above.
  4. If i get a positive result when calling the authenticate endpoint from my Firebase function i assume the token was valid and use the userId in the data returned to create a custom token that is sent back to the user
    1. Use this custom token to sign the user in by calling firebase.auth().signInWithCustomToken(token)

Custom token link: https://firebase.google.com/docs/auth/admin/create-custom-tokens#create_custom_tokens_using_the_firebase_admin_sdk

However the questions above still remain:

  1. Is it possible to store a user session other than in the context of firebase persist, and then "activate" the user session again using this session-data when a user comes back? something like signInWithRefreshToken?
  2. Is there a built in way to keep the users signed in and store it in a place like chrome.storage? (answer seems to be no).
  3. Is there a way to store obfuscated user signin info in a chrome.storage.sync that can be considered to be safe enough? The info is used on page load and deleted when the credentials are no longer valid or the user signs out.

**SECOND UPDATE **

When i run the following script on a testsite hosted on Firebase and with the extension installed locally and not throught the chrome web store, i get the persisted firebase session including refresh token:

        const request = window.indexedDB.open("firebaseLocalStorageDb", 1);
        request.onerror = function (event) {
            console.err("error fetching data", event);
        };
        request.onsuccess = function (dbEvent) {
            const db = request.result;
            const transaction = db.transaction(["firebaseLocalStorage"]);
            console.log({transaction});
            const objectStore = transaction.objectStore("firebaseLocalStorage");
            if ('getAll' in objectStore) {
                objectStore.getAll().onsuccess = function (getAllEvent) {
                    console.log(event.target.result);
                };
            }
        };

In addition, when i inspect the indexeddb using the debugger window the indexeddb of the extension has the security origin set to the page it is loaded through.

screnshot of the security origin in the chrome debugger window

N4TT
  • 141
  • 2
  • 6
  • Why is `indexedDB` not safe? How can another app access your cross origin chrome extension and read `indexedDB` without you allowing it? Firebase Auth has store the state somewhere where it as client access to. The options are: localStorage, sessionStorage, indexedDB, cookies or in memory. Since the client SDK needs the state, it can't be done with `httpOnly` cookies. – bojeil Feb 21 '19 at 04:27
  • When i use persist with firebase it creates an indexeddb, and the security origin of the database (according to the dev tools in chrome) is the domain of the page i am visiting and not the chrome addon. In addition, i have created a small script for getting data from the database and i am indeed able to retrieve the firebase user session object with refresh key and apikey etc etc. – N4TT Feb 22 '19 at 12:24
  • @bojeil see my second update for more info. I haven't seen any docs saying that indexeddb runs in its own context for an extension's content-scripts, do you have any info on this? – N4TT Feb 22 '19 at 12:48
  • What you are claiming is not correct. If you sign in with Firebase Auth from within your chrome extension, the user and its tokens are stored in indexedDB sandboxed by the chrome extension origin (the other domains will not have access to that). I just tested that myself and confirmed it. – bojeil Feb 23 '19 at 22:17
  • @bojeil Thats weird, how did you test it? Are you using a content-script or background script to connect to firebase? Do you have any links to docs for chrome extensions explaining this? I am using a content-script, and even tho the docs state that extensions run in an "isolated world" that does not include ** indexedDB** as far as i can tell. However my technical issues would be gone if you are correct, so i hope i am wrong – N4TT Feb 23 '19 at 23:05
  • I created a browser_action popup. In that page, I load the Firebase SDK CDN, I then sign in the user with email and password. I then inspect the content of the popup via developer console tools and check indexedDB. The user record was stored there and It was sandboxed to origin chrome-extension://MY_CHROME_EXTENSION_ID – bojeil Feb 24 '19 at 00:57
  • @bojeil Ok, so then we are doing different things. I am using a content-script and initializing Firebase from within that script. And there, the data is stored in the indexedDB of the page from what i can tell (see the screenshot at the bottom of my update 2. In that screenshot, my chrome extension's firebase sesssion has "https://nytimes.com" as its security origin). I have however come to the conclusion that i need to handle the session in a background script, and log the user in through a popup, and only keep the display-logic in my content-script. Anyways, thanks the help :) – N4TT Feb 24 '19 at 12:09

0 Answers0