providers/oauth/oauth.remotepageauth.js
/*jslint indent:2,browser:true, node:true */
var PromiseCompat = require('es6-promise').Promise;
var oAuthRedirectId = 'freedom.oauth.redirect.handler';
var TIMEOUT = 5000;
function RemotePageAuth() {
"use strict";
this.listeners = {};
}
/**
* Indicate the intention to initiate an oAuth flow, allowing an appropriate
* oAuth provider to begin monitoring for redirection.
*
* @method initiateOAuth
* @param {string[]} redirectURIs - oAuth redirection URIs registered with the
* provider.
* @param {Function} continuation - Function to call when complete
* Expected to see a value of schema: {{redirect:String, state:String}}
* where 'redirect' is the chosen redirect URI
* and 'state' is the state to pass to the URI on completion of oAuth
* @return {Boolean} true if can handle, false otherwise
*/
RemotePageAuth.prototype.initiateOAuth = function(redirectURIs, continuation) {
"use strict";
if (typeof global !== 'undefined' && global && global.document) {
for (var i=0; i<redirectURIs.length; i++) {
// TODO: remove restriction on URL pattern match.
if ((redirectURIs[i].indexOf('http://') === 0 ||
redirectURIs[i].indexOf('https://') === 0) &&
redirectURIs[i].indexOf('oauth-relay.html') > 0) {
continuation({
redirect: redirectURIs[i],
state: oAuthRedirectId + Math.random()
});
return true;
}
}
}
return false;
};
/**
* oAuth client-side flow - launch the provided URL
* This must be called after initiateOAuth with the returned state object
*
* @method launchAuthFlow
* @param {String} authUrl - The URL that initiates the auth flow.
* @param {Object.<string, string>} stateObj - The return value from initiateOAuth
* @param {Boolean} interactive - Whether to launch an interactive flow
* @param {Function} continuation - Function to call when complete
* Expected to see a String value that is the response Url containing the access token
*/
RemotePageAuth.prototype.launchAuthFlow = function(authUrl, stateObj, interactive, continuation) {
"use strict";
var frame = global.document.createElement('iframe');
frame.src = stateObj.redirect;
frame.style.display = 'none';
global.document.body.appendChild(frame);
frame.addEventListener('load', function () {
this.listeners[stateObj.state] = continuation;
window.open(authUrl);
frame.contentWindow.postMessage(stateObj.state, '*');
}.bind(this));
var hasCredentials = false;
window.addEventListener('message', function (frame, msg) {
if (msg.data && msg.data.key && msg.data.url && this.listeners[msg.data.key]) {
hasCredentials = true;
this.listeners[msg.data.key](msg.data.url);
delete this.listeners[msg.data.key];
try {
document.body.removeChild(frame);
} catch (e) {
console.warn(e);
}
}
}.bind(this, frame), false);
if (interactive === false) {
setTimeout(function() {
if (hasCredentials === false) {
continuation(undefined, 'Error launching auth flow');
delete this.listeners[stateObj.state];
try {
document.body.removeChild(frame);
} catch (e) {
console.warn(e);
}
}
}.bind(this), TIMEOUT);
}
};
/**
* If we have a local domain, and freedom.js is loaded at startup, we can use
* the local page as a redirect URI.
*/
module.exports = RemotePageAuth;