Skip to content
Merged
68 changes: 0 additions & 68 deletions oauth_patch.js

This file was deleted.

9 changes: 6 additions & 3 deletions package.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
Package.describe({
summary: 'A fix for Meteor OAuth with Phonegap'
summary: 'A fix for Meteor OAuth with Phonegap'
});

Package.on_use(function (api) {
api.add_files(["oauth_patch.js"], "client");
});
api.use(['logging', 'oauth'], 'server');
api.use(['oauth', 'accounts-oauth'], 'client');

api.add_files('patch_window.js', 'client');
api.add_files('patch_login_response.js', 'server');
});
42 changes: 42 additions & 0 deletions patch_login_response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Patch OAuth._endOfLoginResponse to expose the credentialToken and
// credentialSecret in the url for the InAppBrowser to retrieve.
// See https://groups.google.com/forum/#!topic/meteor-core/Ma3XTZk4Kqg and
// https://github.com/meteor/meteor/blob/3dfcb42eac34f40b6611a0a0a780b9210467ccb2/packages/oauth/oauth_server.js#L219-L255
OAuth._endOfLoginResponse = function (res, details) {
res.writeHead(200, {'Content-Type': 'text/html'});

var content = function (setCredentialSecret) {
return '<html><head><script>' +
setCredentialSecret +
'window.close()</script></head></html>';
};

if (details.error) {
Log.warn("Error in OAuth Server: " +
(details.error instanceof Error ?
details.error.message : details.error));
res.end(content(""), 'utf-8');
return;
}

if ("close" in details.query) {
// If we have a credentialSecret, report it back to the parent
// window, with the corresponding credentialToken. The parent window
// uses the credentialToken and credentialSecret to log in over DDP.
var setCredentialSecret = '';
if (details.credentials.token && details.credentials.secret) {
setCredentialSecret = 'var credentialToken = ' +
JSON.stringify(details.credentials.token) + ';' +
'var credentialSecret = ' +
JSON.stringify(details.credentials.secret) + ';' +
'if (window.opener) window.opener.Package.oauth.OAuth._handleCredentialSecret(' +
'credentialToken, credentialSecret);' +
// add the token and secret to the hash
'else window.location.hash = "credentialToken=" + credentialToken + ' +
'"&credentialSecret=" + credentialSecret;';
}
res.end(content(setCredentialSecret), "utf-8");
} else {
res.end("", "utf-8");
}
};
112 changes: 112 additions & 0 deletions patch_window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Meteor's OAuth flow currently only works with popups. Phonegap does
// not handle this well. Using the InAppBrowser plugin we can load the
// OAuth popup into it. Using the plugin by itself would not work with
// the MeteorRider phonegap method, this fixes it. This has not been
// tested on other Meteor phonegap methods. (tested on PG 3.3, android, iOS)
//
// http://docs.phonegap.com/en/3.3.0/cordova_inappbrowser_inappbrowser.md.html
// https://github.com/zeroasterisk/MeteorRider
window.patchWindow = function () {
// Prevent the window from being patched twice.
if (window.IAB) return;

// Make sure the InAppBrowser is loaded before patching the window.
try {
window.cordova.require('org.apache.cordova.inappbrowser.inappbrowser');
} catch (e) {
return false;
}

// Plugin messages are not processed on Android until the next
// message this prevents the oauthWin event listeners from firing.
// Call exec on an interval to force process messages.
// http://stackoverflow.com/q/23352940/230462 and
// http://stackoverflow.com/a/24319063/230462
if (device.platform === 'Android') {
setInterval(function () {
cordova.exec(null, null, '', '', [])
}, 200);
}

// Keep a reference to the in app browser's window.open.
var __open = window.open,
oauthWin;

// Create an object to return from a monkeypatched window.open call. Handles
// open/closed state of popup to keep Meteor happy. Allows one to save window
// reference to a variable and close it later. e.g.,
// `var foo = window.open('url'); foo.close();
window.IAB = {
closed: true,

open: function (url) {
// XXX add options param and append to current options
oauthWin = __open(url, '_blank', 'location=no,hidden=yes');

oauthWin.addEventListener('loadstop', checkIfOauthIsDone);

// Close the InAppBrowser on exit -- triggered when the
// user goes back when there are not pages in the history.
oauthWin.addEventListener('exit', close);

oauthWin.show();

function close() {
// close the window
IAB.close();

// remove the listeners
oauthWin.removeEventListener('loadstop', checkIfOauthIsDone);
oauthWin.removeEventListener('exit', close);
}

// check if uri contains an error or code param, then manually close popup
function checkIfOauthIsDone(event) {
// if this is the oauth prompt url, we are not done
if (url === event.url) return;

if (!event.url || !event.url.match(/close|error|code=/)) return;

if (event.url.indexOf('credentialToken') > -1) {
// Get the credentialToken and credentialSecret from the InAppBrowser's url hash.
var hashes = event.url.slice(event.url.indexOf('#') + 1).split('&');
var credentialToken = hashes[0].split('=')[1];

if (event.url.indexOf('credentialSecret') > -1) {
var credentialSecret = hashes[1].split('=')[1];
OAuth._handleCredentialSecret(credentialToken, credentialSecret);
}

Accounts.oauth.tryLoginAfterPopupClosed(credentialToken);
}

close();
}

this.closed = false;
},

close: function () {
if (!oauthWin) return;

oauthWin.close();

this.closed = true;
}
};

// monkeypatch window.open on the phonegap platform
if (typeof device !== "undefined") {
window.open = function (url) {
IAB.open(url);
// return InAppBrowser so you can set foo = open(...) and then foo.close()
return IAB;
};
}

return true;
};

// Patch the window after cordova is finished loading
// if the InAppBrowser is not available yet.
if (!window.patchWindow()) document.addEventListener('deviceready', window.patchWindow, false);