235 lines
7.5 KiB
JavaScript
235 lines
7.5 KiB
JavaScript
;(function (exports) {
|
|
'use strict';
|
|
|
|
var OAUTH3 = exports.OAUTH3;
|
|
var OAUTH3_CORE = exports.OAUTH3_CORE;
|
|
|
|
function getDefaultAppUrl() {
|
|
console.warn('[deprecated] using window.location.{protocol, host, pathname} when opts.appUrl should be used');
|
|
return window.location.protocol
|
|
+ '//' + window.location.host
|
|
+ (window.location.pathname).replace(/\/?$/, '')
|
|
;
|
|
}
|
|
|
|
var browser = exports.OAUTH3_BROWSER = {
|
|
discover: function (providerUri, opts) {
|
|
opts = opts || {};
|
|
opts.debug = true;
|
|
console.log('discover providerUri', providerUri);
|
|
providerUri = OAUTH3_CORE.normalizeUrl(providerUri);
|
|
var discObj = OAUTH3_CORE.discover(providerUri, { appUrl: (opts.appUrl || getDefaultAppUrl()), debug: opts.debug });
|
|
|
|
return browser.insertIframe(discObj.url, discObj.state, opts).then(function (params) {
|
|
if (params.error) {
|
|
return OAUTH3_CORE.formatError(providerUri, params.error);
|
|
}
|
|
var directives = JSON.parse(atob(OAUTH3_CORE.utils.urlSafeBase64ToBase64(params.result || params.directives)));
|
|
directives.issuer = directives.issuer || OAUTH3_CORE.normalizeUrl(providerUri);
|
|
return directives;
|
|
}, function (err) {
|
|
return OAUTH3.PromiseA.reject(err);
|
|
});
|
|
}
|
|
|
|
, frameRequest: function (url, state, opts) {
|
|
var promise;
|
|
|
|
if ('background' === opts.type) {
|
|
promise = browser.insertIframe(url, state, opts);
|
|
} else if ('popup' === opts.type) {
|
|
promise = browser.openWindow(url, state, opts);
|
|
} else {
|
|
throw new Error("login framing method not specified or not type yet implemented");
|
|
}
|
|
|
|
return promise.then(function (params) {
|
|
var err;
|
|
|
|
if (params.error || params.error_description) {
|
|
err = new Error(params.error_description || "Unknown response error");
|
|
err.code = params.error || "E_UKNOWN_ERROR";
|
|
err.params = params;
|
|
return OAUTH3.PromiseA.reject(err);
|
|
}
|
|
|
|
return params;
|
|
});
|
|
}
|
|
|
|
, insertIframe: function (url, state, opts) {
|
|
opts = opts || {};
|
|
var promise = new OAUTH3.PromiseA(function (resolve, reject) {
|
|
var tok;
|
|
var $iframe;
|
|
|
|
function cleanup() {
|
|
delete window['--oauth3-callback-' + state];
|
|
$iframe.remove();
|
|
clearTimeout(tok);
|
|
tok = null;
|
|
}
|
|
|
|
window['--oauth3-callback-' + state] = function (params) {
|
|
console.info('[iframe] complete', params);
|
|
resolve(params);
|
|
cleanup();
|
|
};
|
|
|
|
tok = setTimeout(function () {
|
|
var err = new Error("the iframe request did not complete within 15 seconds");
|
|
err.code = "E_TIMEOUT";
|
|
reject(err);
|
|
cleanup();
|
|
}, opts.timeout || 15000);
|
|
|
|
// TODO hidden / non-hidden (via directive even)
|
|
var framesrc = '<iframe src="' + url + '" ';
|
|
if (opts.debug) {
|
|
framesrc += ' width="800px" height="800px" style="opacity: 0.8;" frameborder="1"';
|
|
}
|
|
else {
|
|
framesrc += ' width="1px" height="1px" frameborder="0"';
|
|
}
|
|
framesrc += '></iframe>';
|
|
$iframe = $(framesrc);
|
|
|
|
$('body').append($iframe);
|
|
});
|
|
|
|
// TODO periodically garbage collect expired handlers from window object
|
|
return promise;
|
|
}
|
|
|
|
, openWindow: function (url, state, opts) {
|
|
var promise = new OAUTH3.PromiseA(function (resolve, reject) {
|
|
var winref;
|
|
var tok;
|
|
|
|
function cleanup() {
|
|
delete window['--oauth3-callback-' + state];
|
|
clearTimeout(tok);
|
|
tok = null;
|
|
// this is last in case the window self-closes synchronously
|
|
// (should never happen, but that's a negotiable implementation detail)
|
|
//winref.close();
|
|
}
|
|
|
|
window['--oauth3-callback-' + state] = function (params) {
|
|
//console.info('[popup] (or window) complete', params);
|
|
resolve(params);
|
|
cleanup();
|
|
};
|
|
|
|
tok = setTimeout(function () {
|
|
var err = new Error("the windowed request did not complete within 3 minutes");
|
|
err.code = "E_TIMEOUT";
|
|
reject(err);
|
|
cleanup();
|
|
}, opts.timeout || 3 * 60 * 1000);
|
|
|
|
// TODO allow size changes (via directive even)
|
|
winref = window.open(
|
|
url
|
|
, 'oauth3-login-' + state
|
|
, 'height=' + (opts.height || 720) + ',width=' + (opts.width || 620)
|
|
);
|
|
if (!winref) {
|
|
reject("TODO: open the iframe first and discover oauth3 directives before popup");
|
|
cleanup();
|
|
}
|
|
});
|
|
|
|
// TODO periodically garbage collect expired handlers from window object
|
|
return promise;
|
|
}
|
|
|
|
//
|
|
// Logins
|
|
//
|
|
, logins: {
|
|
authorizationRedirect: function (providerUri, opts) {
|
|
// TODO get own directives
|
|
|
|
return OAUTH3.discover(providerUri, opts).then(function (directive) {
|
|
var prequest = OAUTH3_CORE.authorizationRedirect(
|
|
directive
|
|
, opts
|
|
);
|
|
|
|
console.log('[DEBUG] [core] authorizationRedirect URL:', prequest);
|
|
|
|
if (!prequest.state) {
|
|
throw new Error("[Devolper Error] [authorization redirect] prequest.state is empty");
|
|
}
|
|
|
|
return browser.frameRequest(prequest.url, prequest.state, opts);
|
|
});
|
|
}
|
|
, implicitGrant: function (providerUri, opts) {
|
|
// TODO OAuth3 provider should use the redirect URI as the appId?
|
|
return OAUTH3.discover(providerUri, opts).then(function (directive) {
|
|
var prequest = OAUTH3_CORE.implicitGrant(
|
|
directive
|
|
// TODO OAuth3 provider should referrer / referer / origin as the appId?
|
|
, opts
|
|
);
|
|
|
|
console.log('[DEBUG] [core] implicitGrant URL', prequest);
|
|
|
|
if (!prequest.state) {
|
|
throw new Error("[Devolper Error] [implicit grant] prequest.state is empty");
|
|
}
|
|
|
|
return browser.frameRequest(prequest.url, prequest.state, opts);
|
|
});
|
|
}
|
|
, logout: function (providerUri, opts) {
|
|
opts = opts || {};
|
|
|
|
return OAUTH3.discover(providerUri, opts).then(function (directive) {
|
|
var prequest = OAUTH3_CORE.logout(
|
|
directive
|
|
, opts
|
|
);
|
|
// Oauth3.init({ logout: function () {} });
|
|
//return Oauth3.logout();
|
|
|
|
var redirectUri = opts.redirectUri
|
|
|| (window.location.protocol + '//' + (window.location.host + window.location.pathname) + 'oauth3.html')
|
|
;
|
|
var params = {
|
|
// logout=true for all logins/accounts
|
|
// logout=app-scoped-login-id for a single login
|
|
action: 'logout'
|
|
// TODO specify specific accounts / logins to delete from session
|
|
, accounts: true
|
|
, logins: true
|
|
, redirect_uri: redirectUri
|
|
, state: prequest.state
|
|
};
|
|
|
|
//console.log('DEBUG oauth3.logout NIX insertIframe');
|
|
//console.log(url, params.redirect_uri);
|
|
//console.log(state);
|
|
//console.log(params); // redirect_uri
|
|
//console.log(opts);
|
|
|
|
if (prequest.url === params.redirect_uri) {
|
|
return OAUTH3.PromiseA.resolve();
|
|
}
|
|
|
|
prequest.url += '#' + OAUTH3_CORE.querystringify(params);
|
|
|
|
return OAUTH3.insertIframe(prequest.url, prequest.state, opts);
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
Object.keys(browser).forEach(function (key) {
|
|
OAUTH3[key] = browser[key];
|
|
});
|
|
|
|
}('undefined' !== typeof exports ? exports : window));
|