4

Had to ask this question as I couldn't find the correct way of asking Google.

I am building a browser extension that requires the user to login using his/her credentials.

Lets keep the OAuth2 way aside for a moment there. After logging in, I am storing the JWT token received from the server in the local storage.

Now when the user navigates to another website, the extension does not have access to the stored local storage data due to cross domain access restriction.

I would like to know if there is any way to maintain the session across multiple domain. Can't ask the user to keep on logging in for every other site, he/she navigates to.

Anywhere else we can store the token to make it accessible everywhere?

Edit:

  1. For the storage via content script, have used chrome.storage.local.
  2. On page load, the content script sets the value from the chrome.storage.local into window.localStorage, if any.
  3. An iframe is embedded into the web page. Display none by default. Iframe does not have any URL set.
  4. Then User clicks on the browser Action button, the iframe is displayed.
  5. User enter the login credentials. These are captured by the script file loaded in the head section of that iframe.
  6. Now on submit, AJAX call is made and then on its success, stores the JWT token in the storage via window.localStorage.

Here, I also want to store the same token in the chrome.storage.local so that when the page is refresh or navigated to another domain, the Step 2 from above will execute and set the window.localStorage with the same token as the previous one has. This way, user is logged in already.

Anything wrong with the above? If not, then how to send the token from the iframe to the content or background script?

bsinky
  • 515
  • 5
  • 15
Kunal Dethe
  • 1,254
  • 1
  • 18
  • 38
  • Which local storage are you using? `localStorage` (website based) is not the same as `chrome.storage.local` (extension based). See [this question and answers](https://stackoverflow.com/questions/24279495/window-localstorage-vs-chrome-storage-local). – Iván Nokonoko Feb 10 '18 at 23:00
  • @IvánNokonoko I did try both `localStorage` and `chrome.storage.local`. Give me some time, will check `chrome.storage.local` again and get back as I didn't understand the difference before. – Kunal Dethe Feb 11 '18 at 11:24
  • @IvánNokonoko The `chrome.storage.local` does work from the extension page but I also have an iframe that loads on click of the browser action button and the script file loaded within also needs access to the same value set by the content script. How to do that? – Kunal Dethe Feb 12 '18 at 07:58
  • @IvánNokonoko I know that the iframe will have access only to the `HTML5 localStorage` but then should I be storing the data in both storage and sync then? – Kunal Dethe Feb 12 '18 at 08:00
  • The iframe cannot access `chrome.storage.local` directly, but it can communicate with the extension vía [messaging](https://developer.chrome.com/extensions/messaging#external-webpage) and request the data from the extension. That way, `chrome.storage.local` would be enough. – Iván Nokonoko Feb 12 '18 at 17:38
  • @IvánNokonoko reached till the part of setting the data via `chrome.storage.local` in content script and `localStorage` in iframe. But now when the data is set via iframe script, I need to send the same data to either content script or background script to that it can further set it via `chrome.storage.local`, it is not working. I know there must be something I am missing. Tried via `window.parent.postMessage()` too. – Kunal Dethe Feb 13 '18 at 19:00
  • @IvánNokonoko I have updated my question, please check. – Kunal Dethe Feb 15 '18 at 15:32

1 Answers1

6

I would use a background script. Therefore add

"background": {
    "scripts": [ "background.bundle.js" ],
},

to your manifest.json.

Basically a background script is like an extra browser window instance, just running in the background concurrent to all active sites. Here you can find an explanation how to send messages between your active sites (content script) and the background script of your application: https://developer.chrome.com/extensions/messaging

Explaining all of this goes beyond the scope of this answer.

Basically you are interested in the "Long-lived connections" section. You should first establish a connection between your content script and your background script. If an event (like login) occures in the content script you can use the connection to notify the background script or vice versa.

In your background script you have an object chrome.store.local, which basically is the same as your local storage. There you can save your JWT.

Via the message passing you can persist the JWT in the background script and access it (when you navigate) through the message system in every window, and therefore it is domain independent. Certainly you can also check if the domain is the right one in your content script.

EDIT:

As I understand your question, your problem is to get the token out of the iframe. I am not sure how your authentication in the iframe works, but lets assume it is a form.

Then you can access the DOM of the iframe via:

my_iframe.contentWindow.document
      .getElementById("myForm")
      .onsubmit = function() { /* todo */ }

If you are not the creator of the iframe you should probably wrap the onsubmit function, by something like this:

var my_form = my_iframe.contentWindow.document
      .getElementById("myForm")
var old_handler = my_form.onsubmit
my_form.onsubmit = function() {
    /* todo */
    if(old_handler != undefined) {
        var result = old_handler.apply(this, arguments)
    }
    /* todo (maybe send to background) */
}

Otherwise you can just delegate the event to your background / content script. Your background script can than do the authentication.

var my_form = my_iframe.contentWindow.document
      .getElementById("myForm")
      .onsubmit = function() {
           var result = authenticate_background(arguments)
       }

Here the authenticate_background sends the arguments to the background script. Keep in mind that you cannot send dom elements to the background script. Therefore you first must serialize the arguments.

In general I do not like the approach of using an iframe, but if you have a given authentication provider this could be the only viable option. Also there might be a more elegant way than wrapping the onsubmit function. Maybe the script running in the iframe has a special mechanism (callback) where you can register your authentication.

Arwed Mett
  • 2,614
  • 1
  • 22
  • 35
  • Thank you for the answer. The message communication between the `content script` and the `background.js` (in my case), is working fine. I am getting the issue with sending the message from the `script.js` loaded in the `iframe` to either `content script` or the `background.js`. Will update the question with recent attempted piece of code for more clarification. – Kunal Dethe Feb 15 '18 at 15:21
  • hope this helps ;) – Arwed Mett Feb 15 '18 at 16:00
  • @KunalDethe If this is the answer you are looking for, could you mark it. – Arwed Mett Feb 16 '18 at 11:35
  • Not exactly. I need the way to send the `token` string to the `content` or `background` script. So need the actual code snippet that does that and also the one which will listen / receive the token. I have the idea as how the flow should be, just the code isn't working, so I must be writing it wrong. Need to know the exact example. I do have the `background.bundle.js` loaded already and am trying to receive the message from the `iframe` but am not able to. I do no want to use the `submit` handler in the `background script`. Need to send it like a `sendMessage` or `postMessage`. – Kunal Dethe Feb 17 '18 at 18:28
  • This is not trivial and the API makes it rather hard to pass messages around. I recovered some of my old code and created a small example (https://github.com/Pfeifenjoy/chrome-extension-message). It does not satisfy all of your requirements. You may will have to extend it. – Arwed Mett Feb 17 '18 at 20:17