Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Google Drive from a Firefox extension

I'm trying to access (CRUD) Google Drive from a Firefox extension. Extensions are coded in Javascript, but neither of the two existing javascript SDKs seem to fit; the client-side SDK expects "window" to be available, which isn't the case in extensions, and the server-side SDK seems to rely on Node-specific facilities, as a script that works in node no longer does when I load it in chrome after running it through browserify. Am I stuck using raw REST calls? The Node script that works looks like this:

var google = require('googleapis');
var readlineSync = require('readline-sync');

var CLIENT_ID = '....',
    CLIENT_SECRET = '....',
    REDIRECT_URL = 'urn:ietf:wg:oauth:2.0:oob',
    SCOPE = 'https://www.googleapis.com/auth/drive.file';

var oauth2Client = new google.auth.OAuth2(CLIENT_ID, CLIENT_SECRET, REDIRECT_URL);

var url = oauth2Client.generateAuthUrl({
  access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
  scope: SCOPE // If you only need one scope you can pass it as string
});

var code = readlineSync.question('Auth code? :');

oauth2Client.getToken(code, function(err, tokens) {
  console.log('authenticated?');
  // Now tokens contains an access_token and an optional refresh_token. Save them.
  if(!err) {
    console.log('authenticated');
    oauth2Client.setCredentials(tokens);
  } else {
    console.log('not authenticated');
  }
});

I wrap the node GDrive SDK using browserify on this script:

var Google = new function(){
    this.api = require('googleapis');
    this.clientID = '....';
    this.clientSecret = '....';
    this.redirectURL = 'urn:ietf:wg:oauth:2.0:oob';
    this.scope = 'https://www.googleapis.com/auth/drive.file';
    this.client = new this.api.auth.OAuth2(this.clientID, this.clientSecret, this.redirectURL);
  }
}

which is then called using after clicking a button (if the text field has no code it launches the browser to get one):

function authorize() {
  var code = document.getElementById("code").value.trim();

  if (code === '') {
    var url = Google.client.generateAuthUrl({access_type: 'offline', scope: Google.scope});
    var win = Components.classes['@mozilla.org/appshell/window-mediator;1'].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow('navigator:browser');
    win.gBrowser.selectedTab = win.gBrowser.addTab(url);
  } else {
    Google.client.getToken(code, function(err, tokens) {
      if(!err) {
        Google.client.setCredentials(tokens);
        // store token
        alert('Succesfully authorized');
      } else {
        alert('Not authorized: ' + err); // always ends here
      }
    });
  }
}

But this yields the error Not authorized: Invalid protocol: https:

like image 668
retorquere Avatar asked Sep 11 '14 14:09

retorquere


People also ask

Can you access Google Drive through Firefox?

You have nothing to worry about. Firefox works great with Google Docs, Hangouts and G Suite.

Is there a Google Drive Plugin?

Open with Google Drive Viewer This extension can be convenient for viewing documents and other files that are on the web without having to download them first.

Can Chrome extensions transfer to Firefox?

Once you have the Chrome extension page open inside Firefox browser, click on the Add to Firefox button to add it. Note that while you are on Chrome extension page inside Firefox, you should not see the incompatible or open this in Chrome message. Instead, Foxified will show an option to add to Firefox.


2 Answers

It is possible though, depending on the use case, it might also of limited interest.

Firefox ships with a tiny http server, just the bare bones. It is included for test purposes but this is not a reason to overlook it.

Lets follow the quickstart guide for running a Drive app in Javascript

The tricky part is to set the Redirect URIs and the Javascript Origins. Obviously the right setting is http://localhost, but how can you be sure that every user has port 80 available?

You can't and, unless you have control over your users, no port is guaranteed to work for everyone. With this in mind lets choose port 49870 and pray.

So now Redirect URIs and the Javascript Origins are set to http://localhost:49870

Assuming you use Add-on SDK, save the quickstart.html (remember to add your Client ID) in the data directory of your extension. Now edit your main.js

const self = require("sdk/self");
const { Cc, Ci } = require("chrome");
const tabs = require("sdk/tabs");
const httpd = require("sdk/test/httpd");

var quickstart = self.data.load("quickstart.html");

var srv = new httpd.nsHttpServer();

srv.registerPathHandler("/gdrive", function handler(request, response){
  response.setHeader("Content-Type", "text/html; charset=utf-8", false);

  let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(Ci.nsIScriptableUnicodeConverter);
  converter.charset = "UTF-8";
  response.write(converter.ConvertFromUnicode(quickstart));
})

srv.start(49870);

tabs.open("http://localhost:49870/gdrive");

exports.onUnload = function (reason) {
  srv.stop(function(){});
};

Notice that quickstart.html is not opened as a local file, with a resource: URI. The Drive API wouldn't like that. It is served at the url http://localhost:49870/gdrive. Needless to say that instead of static html we can use a template or anything else. Also the http://localhost:49870/gdrive can be scripted with a regular PageMod.

I don't consider this a real solution. It's just better than nothing.

like image 147
paa Avatar answered Oct 09 '22 07:10

paa


From here https://developer.mozilla.org/en/docs/Working_with_windows_in_chrome_code you could try window = window || content || {}

Use the JavaScript client API and not the node.js client. Although browserify will make it work. You will have to expose your client secret in the latter. The flow of client side authentication is very diff than server side. Refer to https://developers.google.com/accounts/docs/OAuth2

Having said all this. Its really not that difficult to implement an app with REST based calls. The methods in all client libraries mimic the corresponding REST URLs. You could set up some functions of your own to handle request and response and the rest would feel the same.

like image 1
Gaurav Ramanan Avatar answered Oct 09 '22 07:10

Gaurav Ramanan