diff --git a/oauth3.browser.js b/oauth3.browser.js
new file mode 100644
index 0000000..b0ceb70
--- /dev/null
+++ b/oauth3.browser.js
@@ -0,0 +1,234 @@
+;(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 = $(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));
diff --git a/oauth3.cache.js b/oauth3.cache.js
new file mode 100644
index 0000000..6b5074a
--- /dev/null
+++ b/oauth3.cache.js
@@ -0,0 +1,42 @@
+ oauth3.discover = function (providerUri, opts) {
+ opts = opts || {};
+
+ console.log('DEBUG oauth3.discover', providerUri);
+ console.log(opts);
+ if (opts.directives) {
+ return oauth3.PromiseA.resolve(opts.directives);
+ }
+
+ var promise;
+ var promise2;
+ var directives;
+ var updatedAt;
+ var fresh;
+
+ providerUri = oauth3.normalizeUrl(providerUri);
+ try {
+ directives = JSON.parse(localStorage.getItem('oauth3.' + providerUri + '.directives'));
+ console.log('DEBUG oauth3.discover cache', directives);
+ updatedAt = localStorage.getItem('oauth3.' + providerUri + '.directives.updated_at');
+ console.log('DEBUG oauth3.discover updatedAt', updatedAt);
+ updatedAt = new Date(updatedAt).valueOf();
+ console.log('DEBUG oauth3.discover updatedAt', updatedAt);
+ } catch(e) {
+ // ignore
+ }
+
+ fresh = (Date.now() - updatedAt) < (24 * 60 * 60 * 1000);
+
+ if (directives) {
+ promise = oauth3.PromiseA.resolve(directives);
+
+ if (fresh) {
+ //console.log('[local] [fresh directives]', directives);
+ return promise;
+ }
+ }
+
+ promise2 = oauth3._discoverHelper(providerUri, opts);
+
+ return promise || promise2;
+ };
diff --git a/oauth3.core.js b/oauth3.core.js
index e796a36..61e0cae 100644
--- a/oauth3.core.js
+++ b/oauth3.core.js
@@ -1,4 +1,4 @@
-(function (exports) {
+;(function (exports) {
'use strict';
// NOTE: we assume that directive.provider_uri exists
@@ -75,6 +75,35 @@
return argsParsed;
};
+ core.formatError = function (providerUri, params) {
+ var err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
+ err.uri = params.error_uri;
+ err.code = params.error;
+ return err;
+ };
+ core.normalizeUri = function (providerUri) {
+ // tested with
+ // example.com
+ // example.com/
+ // http://example.com
+ // https://example.com/
+ return providerUri
+ .replace(/^(https?:\/\/)?/i, '')
+ .replace(/\/?$/, '')
+ ;
+ };
+ core.normalizeUrl = function (providerUri) {
+ // tested with
+ // example.com
+ // example.com/
+ // http://example.com
+ // https://example.com/
+ return providerUri
+ .replace(/^(https?:\/\/)?/i, 'https://')
+ .replace(/\/?$/, '')
+ ;
+ };
+
core.discover = function (providerUri, opts) {
if (!providerUri) {
throw new Error("cannot discover without providerUri");
@@ -510,6 +539,40 @@
};
};
+ core.logout = function (directive, opts) {
+ opts = opts || {};
+ var type = 'logout';
+ var clientId = opts.appId || opts.clientId || opts.client_id;
+ var args = directive[type];
+ var params = {
+ client_id: opts.clientUri || opts.client_uri
+ , debug: opts.debug || undefined
+ };
+ var uri = args.url;
+ var body;
+
+ if (opts.clientUri) {
+ params.client_uri = opts.clientUri;
+ }
+
+ if (clientId) {
+ params.client_id = clientId;
+ }
+
+ args.method = (args.method || 'GET').toUpperCase();
+ if ('GET' === args.method) {
+ uri += '?' + core.querystringify(params);
+ } else {
+ body = params;
+ }
+
+ return {
+ url: uri
+ , method: args.method || 'GET'
+ , data: body
+ };
+ };
+
exports.OAUTH3 = exports.OAUTH3 || { core: core };
exports.OAUTH3_CORE = core.OAUTH3_CORE = core;
diff --git a/oauth3.js b/oauth3.js
index c7cc5e3..7cc83cf 100644
--- a/oauth3.js
+++ b/oauth3.js
@@ -7,14 +7,6 @@
var core = exports.OAUTH3_CORE || require('./oauth3.core.js');
- 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(/\/?$/, '')
- ;
- }
-
oauth3.requests = logins;
if ('undefined' !== typeof Promise) {
@@ -149,45 +141,6 @@
*/
};
- logins.authorizationRedirect = function (providerUri, opts) {
- // TODO get own directives
-
- return oauth3.discover(providerUri, opts).then(function (directive) {
- var prequest = core.authorizationRedirect(
- directive
- , opts.authorizationRedirect
- , opts
- );
-
- console.log('[DEBUG] [core] authorizationRedirect URL:', prequest);
-
- if (!prequest.state) {
- throw new Error("[Devolper Error] [authorization redirect] prequest.state is empty");
- }
-
- return oauth3.frameRequest(prequest.url, prequest.state, opts);
- });
- };
-
- logins.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 = 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 oauth3.frameRequest(prequest.url, prequest.state, opts);
- });
- };
-
oauth3.loginCode = function (providerUri, opts) {
return oauth3.discover(providerUri, opts).then(function (directive) {
var prequest = core.loginCode(directive, opts);
@@ -205,19 +158,12 @@
});
};
- logins.resourceOwnerPassword = function (providerUri, username, passphrase, opts) {
- console.log('DEBUG logins.resourceOwnerPassword opts', opts);
+ oauth3.resourceOwnerPassword = function (providerUri, username, passphrase, opts) {
+ console.log('DEBUG oauth3.resourceOwnerPassword opts', opts);
//var scope = opts.scope;
//var appId = opts.appId;
return oauth3.discover(providerUri, opts).then(function (directive) {
- var prequest = core.resourceOwnerPassword(
- directive
- , username
- , passphrase
- , opts // secret: proofstr || otpCode, totp: totpToken, otp: otpCode, otpUuid: otpUuuid
- //, scope
- //, appId
- );
+ var prequest = core.resourceOwnerPassword(directive, opts);
console.log('[DEBUG] [core] resourceOwnerPassword URL', prequest);
@@ -243,29 +189,16 @@
});
};
- oauth3.frameRequest = function (url, state, opts) {
- var promise;
-
- if ('background' === opts.type) {
- promise = oauth3.insertIframe(url, state, opts);
- } else if ('popup' === opts.type) {
- promise = oauth3.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;
- });
+ // TODO It'll be very interesting to see if we can do some browser popup stuff from the CLI
+ logins._error_description = 'Not Implemented: Please override by including ';
+ logins.authorizationRedirect = function (/*providerUri, opts*/) {
+ throw new Error(logins._error_description);
+ };
+ logins.implicitGrant = function (/*providerUri, opts*/) {
+ throw new Error(logins._error_description);
+ };
+ logins.logout = function (/*providerUri, opts*/) {
+ throw new Error(logins._error_description);
};
oauth3.login = function (providerUri, opts) {
@@ -298,7 +231,7 @@
delete opts.username;
delete opts.password;
- return logins.resourceOwnerPassword(providerUri, username, password, opts).then(function (resp) {
+ return oauth3.resourceOwnerPassword(providerUri, username, password, opts).then(function (resp) {
if (!resp || !resp.data) {
var err = new Error("bad response");
err.response = resp;
@@ -336,267 +269,6 @@
return oauth3.login(providerUri, opts);
};
- oauth3.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)
- $iframe = $(
- ''
- + '" width="1px" height="1px" frameborder="0">'
- );
-
- $('body').append($iframe);
- });
-
- // TODO periodically garbage collect expired handlers from window object
- return promise;
- };
-
- oauth3.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=720,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;
- };
-
- oauth3.logout = function (providerUri, opts) {
- opts = opts || {};
-
- // Oauth3.init({ logout: function () {} });
- //return Oauth3.logout();
-
- var state = parseInt(Math.random().toString().replace('0.', ''), 10).toString('36');
- var url = providerUri.replace(/\/$/, '') + (opts.providerOauth3Html || '/oauth3.html');
- 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: 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 (url === params.redirect_uri) {
- return oauth3.PromiseA.resolve();
- }
-
- url += '#' + core.querystringify(params);
-
- return oauth3.insertIframe(url, state, opts);
- };
-
- oauth3.createState = function () {
- // TODO mo' betta' random function
- // maybe gather some entropy from mouse / keyboard events?
- // (probably not, just use webCrypto or be sucky)
- return parseInt(Math.random().toString().replace('0.', ''), 10).toString('36');
- };
-
- oauth3.normalizeProviderUri = function (providerUri) {
- // tested with
- // example.com
- // example.com/
- // http://example.com
- // https://example.com/
- providerUri = providerUri
- .replace(/^(https?:\/\/)?/, 'https://')
- .replace(/\/?$/, '')
- ;
-
- return providerUri;
- };
-
- oauth3._discoverHelper = function (providerUri, opts) {
- return oauth3._discoverHelperNew(providerUri, opts).then(function () {
- }, function () {
- console.warn('[directives] fallback to old /oauth3.html');
- return oauth3._discoverHelperOld(providerUri, opts);
- });
- };
- oauth3._discoverHelperNew = function (providerUri, opts) {
- opts = opts || {};
- var state = oauth3.createState();
- var discObj = oauth3.core.discover(providerUri, { state: state, appUrl: (opts.appUrl || getDefaultAppUrl()) });
-
- return oauth3.insertIframe(discObj.url, state, opts).then(function (directives) {
- return directives;
- }, function (err) {
- return oauth3.PromiseA.reject(err);
- });
- };
- oauth3._discoverHelperOld = function (providerUri, opts) {
- opts = opts || {};
- var state = oauth3.createState();
- var params;
- var url;
-
- params = {
- action: 'directives'
- , state: state
- // TODO this should be configurable (i.e. I want a dev vs production oauth3.html)
- , redirect_uri: window.location.protocol + '//' + window.location.host
- + (window.location.pathname + '/oauth3.html').replace(/\/\//, '/')
- };
-
- url = providerUri + '/oauth3.html#' + core.querystringify(params);
-
- return oauth3.insertIframe(url, state, opts).then(function (directives) {
- return directives;
- }, function (err) {
- return oauth3.PromiseA.reject(err);
- });
- };
-
- oauth3.discover = function (providerUri, opts) {
- opts = opts || {};
-
- console.log('DEBUG oauth3.discover', providerUri);
- console.log(opts);
- if (opts.directives) {
- return oauth3.PromiseA.resolve(opts.directives);
- }
-
- var promise;
- var promise2;
- var directives;
- var updatedAt;
- var fresh;
-
- providerUri = oauth3.normalizeProviderUri(providerUri);
- try {
- directives = JSON.parse(localStorage.getItem('oauth3.' + providerUri + '.directives'));
- console.log('DEBUG oauth3.discover cache', directives);
- updatedAt = localStorage.getItem('oauth3.' + providerUri + '.directives.updated_at');
- console.log('DEBUG oauth3.discover updatedAt', updatedAt);
- updatedAt = new Date(updatedAt).valueOf();
- console.log('DEBUG oauth3.discover updatedAt', updatedAt);
- } catch(e) {
- // ignore
- }
-
- fresh = (Date.now() - updatedAt) < (24 * 60 * 60 * 1000);
-
- if (directives) {
- promise = oauth3.PromiseA.resolve(directives);
-
- if (fresh) {
- //console.log('[local] [fresh directives]', directives);
- return promise;
- }
- }
-
- promise2 = oauth3._discoverHelper(providerUri, opts).then(function (params) {
- console.log('DEBUG oauth3._discoverHelper', params);
- var err;
-
- if (!params.directives) {
- err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
- err.code = params.error || "E_UNKNOWN_ERROR";
- return oauth3.PromiseA.reject(err);
- }
-
- try {
- directives = JSON.parse(atob(params.directives));
- console.log('DEBUG oauth3._discoverHelper directives', directives);
- } catch(e) {
- err = new Error(params.error_description || "could not parse directives for provider '" + providerUri + "'");
- err.code = params.error || "E_PARSE_DIRECTIVE";
- return oauth3.PromiseA.reject(err);
- }
-
- if (
- (directives.authorization_dialog && directives.authorization_dialog.url)
- || (directives.access_token && directives.access_token.url)
- ) {
- // TODO lint directives
- // TODO self-reference in directive for providerUri?
- directives.provider_uri = providerUri;
- localStorage.setItem('oauth3.' + providerUri + '.directives', JSON.stringify(directives));
- localStorage.setItem('oauth3.' + providerUri + '.directives.updated_at', new Date().toISOString());
-
- return oauth3.PromiseA.resolve(directives);
- } else {
- // ignore
- console.error("the directives provided by '" + providerUri + "' were invalid.");
- params.error = params.error || "E_INVALID_DIRECTIVE";
- params.error_description = params.error_description
- || "directives did not include authorization_dialog.url";
- err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
- err.code = params.error;
- return oauth3.PromiseA.reject(err);
- }
- });
-
- return promise || promise2;
- };
-
oauth3.core = core;
oauth3.querystringify = core.querystringify;
oauth3.scopestringify = core.stringifyscope;
diff --git a/oauth3.lint.js b/oauth3.lint.js
index d41722e..36c89a1 100644
--- a/oauth3.lint.js
+++ b/oauth3.lint.js
@@ -1,5 +1,6 @@
+
// TODO move to a test / lint suite?
- oauth3._testPromise = function (PromiseA) {
+ oauth3._lintPromise = function (PromiseA) {
var promise;
var x = 1;
@@ -48,3 +49,44 @@
x = 2;
return promise;
};
+
+ oauth3._lintDirectives = function (providerUri, directives) {
+ var params = { directives: directives };
+ console.log('DEBUG oauth3._discoverHelper', directives);
+ var err;
+ if (!params.directives) {
+ err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
+ err.code = params.error || "E_UNKNOWN_ERROR";
+ return OAUTH3.PromiseA.reject(err);
+ }
+
+ try {
+ directives = JSON.parse(atob(params.directives));
+ console.log('DEBUG oauth3._discoverHelper directives', directives);
+ } catch(e) {
+ err = new Error(params.error_description || "could not parse directives for provider '" + providerUri + "'");
+ err.code = params.error || "E_PARSE_DIRECTIVE";
+ return OAUTH3.PromiseA.reject(err);
+ }
+ if (
+ (directives.authorization_dialog && directives.authorization_dialog.url)
+ || (directives.access_token && directives.access_token.url)
+ ) {
+ // TODO lint directives
+ // TODO self-reference in directive for providerUri?
+ directives.provider_uri = providerUri;
+ localStorage.setItem('oauth3.' + providerUri + '.directives', JSON.stringify(directives));
+ localStorage.setItem('oauth3.' + providerUri + '.directives.updated_at', new Date().toISOString());
+
+ return OAUTH3.PromiseA.resolve(directives);
+ } else {
+ // ignore
+ console.error("the directives provided by '" + providerUri + "' were invalid.");
+ params.error = params.error || "E_INVALID_DIRECTIVE";
+ params.error_description = params.error_description
+ || "directives did not include authorization_dialog.url";
+ err = new Error(params.error_description || "Unknown error when discoving provider '" + providerUri + "'");
+ err.code = params.error;
+ return OAUTH3.PromiseA.reject(err);
+ }
+ };