From 1e459ce1862f7a3b8d0bc021e72120be1626a168 Mon Sep 17 00:00:00 2001 From: John Shaver Date: Wed, 15 Nov 2017 21:39:10 -0800 Subject: [PATCH 1/5] WIP started trying some things with scope discovery. --- oauth3.core.js | 52 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index fdd707b..d5f9ade 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -1,4 +1,4 @@ -/* global Promise */ +/ * global Promise */ ;(function (exports) { 'use strict'; @@ -294,7 +294,11 @@ } } , urls: { - discover: function (providerUri, opts) { + scope: function (directives, opts) { + var uri = directives.grants.url; + uri.replace("/:sub", ); + } + , discover: function (providerUri, opts) { if (!providerUri) { throw new Error("cannot discover without providerUri"); } @@ -658,9 +662,43 @@ } } } + , discoverGrants: function (providerUri, opts) { + if (!providerUri) { + throw new Error('oauth3.discoverGrants(providerUri, opts) received providerUri as :', providerUri); + } + + var opts = opts || {}; + var err; + if(!opts.sub && (!opts.token || !opts.token.sub)) { + err = new Error('We need a sub in order to discover grants for.'); + err.code = 'E_NO_SUB'; + throw err; + } + if(! + + + console.warn(!opts.scopes) { + var firstDo; + + if (directives && directives.issuer) { + firstDo = OAUTH3.PromiseA.resolve(directives); + } else { + firstDo = this.discover(providerUri, opts); + } + + return firstDo.then(function(directives) { + var grantPromises = []; + + + + return OAUTH3._disoverHelper(providerUri, "grants", opts); + }).then(function() { + }); + } + , discover: function (providerUri, opts) { if (!providerUri) { - throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri); + throw new Error('oauth3.discover(providerUri, opts) received providerUri as :', providerUri); } return OAUTH3.hooks.directives.get(providerUri).then(function (directives) { @@ -668,7 +706,7 @@ return directives; } - return OAUTH3._discoverHelper(providerUri, opts).then(function (directives) { + return OAUTH3._discoverHelper(providerUri, "directives", opts).then(function (directives) { directives.azp = directives.azp || OAUTH3.url.normalize(providerUri); directives.issuer = directives.issuer || OAUTH3.url.normalize(providerUri); directives.api = OAUTH3.url.normalize((directives.api||':hostname').replace(/:hostname/, OAUTH3.uri.normalize(directives.issuer) || OAUTH3.uri.normalize(providerUri))); @@ -677,8 +715,8 @@ }); }); } - , _discoverHelper: function(providerUri, opts) { - return OAUTH3._browser.discover(providerUri, opts); + , _discoverHelper: function(providerUri, type, opts) { + return OAUTH3._browser.discover(providerUri, type, opts); } , request: function (preq, opts) { function fetch() { @@ -859,7 +897,7 @@ , _browser: { window: 'undefined' !== typeof window ? window : null // TODO we don't need to include this if we're using jQuery or angular - , discover: function(providerUri, opts) { + , discover: function(providerUri, type, opts) { opts = opts || {}; providerUri = OAUTH3.url.normalize(providerUri); From d13728dd3d6b6554f8518cb1778babea56100a9a Mon Sep 17 00:00:00 2001 From: John Shaver Date: Mon, 20 Nov 2017 08:26:52 -0800 Subject: [PATCH 2/5] Initial scope discover. Needs testing and renaming. --- oauth3.core.js | 78 +++++++++++++++++------------------- well-known/oauth3/index.html | 40 ++++++++++++++++-- 2 files changed, 74 insertions(+), 44 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index d5f9ade..ea2611e 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -294,10 +294,6 @@ } } , urls: { - scope: function (directives, opts) { - var uri = directives.grants.url; - uri.replace("/:sub", ); - } , discover: function (providerUri, opts) { if (!providerUri) { throw new Error("cannot discover without providerUri"); @@ -307,25 +303,27 @@ } var clientId = OAUTH3.url.normalize(opts.client_id || opts.client_uri); providerUri = OAUTH3.url.normalize(providerUri); + var discoverFile = opts.discoverFile || "directives.json"; var params = { - action: 'directives' + action: 'directives' //TODO: change this to not be directive specific. Is it even used? , state: opts.state || OAUTH3.utils.randomState() , redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html#/') , response_type: 'rpc' + , discoverFile: opts.discoveFile || "directives.json" , _method: 'GET' , _pathname: '.well-known/oauth3/directives.json' , debug: opts.debug || undefined }; - var result = { + var toRequest = { url: providerUri + '/.well-known/oauth3/#/?' + OAUTH3.query.stringify(params) , state: params.state , method: 'GET' , query: params }; - return result; + return toRequest; } , implicitGrant: function (directive, opts) { // @@ -534,6 +532,14 @@ return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.clear()); } } + , scopes: { + get: function(providerUri) { + //TODO: retrieve cached scopes + } + , set: function(providerUri, scopes) { + //TODO: cache scopes + } + } , session: { refresh: function (oldSession, newSession) { var providerUri = oldSession.provider_uri; @@ -662,37 +668,18 @@ } } } - , discoverGrants: function (providerUri, opts) { + , discoverScopes: function (providerUri, opts) { if (!providerUri) { - throw new Error('oauth3.discoverGrants(providerUri, opts) received providerUri as :', providerUri); + throw new Error('oauth3.discoverScopes(providerUri, opts) received providerUri as :', providerUri); } var opts = opts || {}; - var err; - if(!opts.sub && (!opts.token || !opts.token.sub)) { - err = new Error('We need a sub in order to discover grants for.'); - err.code = 'E_NO_SUB'; - throw err; - } - if(! + opts.discoverFile = "scopes.json"; + //TODO: add caching - console.warn(!opts.scopes) { - var firstDo; - - if (directives && directives.issuer) { - firstDo = OAUTH3.PromiseA.resolve(directives); - } else { - firstDo = this.discover(providerUri, opts); - } - - return firstDo.then(function(directives) { - var grantPromises = []; - - - - return OAUTH3._disoverHelper(providerUri, "grants", opts); - }).then(function() { + return OAUTH3._discoverHelper(providerUri, opts).then(function(scopes) { + return scopes; }); } @@ -706,7 +693,7 @@ return directives; } - return OAUTH3._discoverHelper(providerUri, "directives", opts).then(function (directives) { + return OAUTH3._discoverHelper(providerUri, opts).then(function (directives) { directives.azp = directives.azp || OAUTH3.url.normalize(providerUri); directives.issuer = directives.issuer || OAUTH3.url.normalize(providerUri); directives.api = OAUTH3.url.normalize((directives.api||':hostname').replace(/:hostname/, OAUTH3.uri.normalize(directives.issuer) || OAUTH3.uri.normalize(providerUri))); @@ -715,8 +702,9 @@ }); }); } - , _discoverHelper: function(providerUri, type, opts) { - return OAUTH3._browser.discover(providerUri, type, opts); + , _discoverHelper: function(providerUri, opts) { + opts.discoverFile = "directives.json"; + return OAUTH3._browser.discover(providerUri, opts); } , request: function (preq, opts) { function fetch() { @@ -897,17 +885,24 @@ , _browser: { window: 'undefined' !== typeof window ? window : null // TODO we don't need to include this if we're using jQuery or angular - , discover: function(providerUri, type, opts) { + , discover: function(providerUri, opts) { opts = opts || {}; providerUri = OAUTH3.url.normalize(providerUri); + // If no discoverFile was specified, who knows what they want, but + // this function used to only support directives.json, so it's worth + // a shot. + var discoverFile = opts.discoverFile || "directives.json"; + if (OAUTH3.uri.normalize(providerUri).replace(/\/.*/, '') === OAUTH3.uri.normalize(OAUTH3._browser.window.location.hostname)) { - console.warn("It looks like you're a provider checking for your own directive," + console.warn("It looks like you're a provider trying to discover on yourself," + " so we we're just gonna use" - + " OAUTH3.request({ method: 'GET', url: '.well-known/oauth3/directive.json' })"); + + " OAUTH3.request({ method: 'GET', url: " + + "'/.well-known/oauth3/" + discoverFile + "' })"); + return OAUTH3.request({ method: 'GET' - , url: OAUTH3.url.normalize(providerUri) + '/.well-known/oauth3/directives.json' + , url: OAUTH3.url.normalize(providerUri) + '/.well-known/oauth3/' + discoverFile }).then(function (resp) { return resp.data; }); @@ -926,6 +921,7 @@ , broker: opts.broker , state: opts._state || undefined , debug: opts.debug + , discoverFile: opts.discoverFile } ); opts._state = discReq.state; @@ -957,9 +953,9 @@ } // TODO params should have response_type indicating json, binary, etc - var directives = JSON.parse(OAUTH3._base64.decodeUrlSafe(params.result || params.directives)); + var result = JSON.parse(OAUTH3._base64.decodeUrlSafe(params.result || params.directives)); // caller will call OAUTH3.hooks.directives.set(providerUri, directives); - return directives; + return result; }); } , request: function (preq, _sys) { diff --git a/well-known/oauth3/index.html b/well-known/oauth3/index.html index 9c415e5..b31b6b9 100644 --- a/well-known/oauth3/index.html +++ b/well-known/oauth3/index.html @@ -30,11 +30,44 @@ console.log(prefix, 'params:'); console.log(params); - OAUTH3.request({ url: 'directives.json' }).then(function (resp) { + var fileWhiteList = [ + "directives.json" + , "scopes.json" ]; + + //Serving arbitrary files/paths is probably not a good idea. + //Let's make sure this is something we want to serve. + if(fileWhiteList.indexOf(params.discoverFile) === -1) { + //Nope! + var redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ + state: params.state + , error: "No access to requested file: " + params.discoverFile + , error_code: "E_ACCESS_DENIED" + , debug: params.debug || undefined + }); + + console.error(prefix, "Requested file is not listed as a discoverable file:" + , fileWhiteList); + console.log("Redirecting with error: ", redirect) + + if (!params.debug) { + window.location = redirect; + } else { + // yes, we're violating the security lint with purpose + document.body.innerHTML += window.location.host + window.location.pathname + + '

You\'ve passed the \'debug\' parameter so we\'re pausing' + + ' to let you look at logs or whatever it is that you intended to do.' + + '

The requested file was not a discoverable file (see console for details).' + + '

Continue with error redirect: ' + redirect + ''; + } + return; + } + + OAUTH3.request({ url: params.discoverfile }).then(function (resp) { var urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0)); var redirect; + var returnParams; - console.log(prefix, 'directives'); + console.log(prefix, 'file contents'); console.log(resp); console.log(prefix, 'base64'); @@ -48,7 +81,8 @@ console.log(prefix, 'params.redirect_uri:', params.redirect_uri); redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ state: params.state - , directives: urlsafe64 + , directives: urlsafe64 //kept for now, probably should remove this. + , result: urlsafe64 , debug: params.debug || undefined }) From bc82bb6f1b01b6bb615f45cefa622754c8e73b78 Mon Sep 17 00:00:00 2001 From: John Shaver Date: Mon, 20 Nov 2017 08:59:23 -0800 Subject: [PATCH 3/5] Moved scopes to well-known dir. --- well-known/oauth3/scopes.json | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 well-known/oauth3/scopes.json diff --git a/well-known/oauth3/scopes.json b/well-known/oauth3/scopes.json new file mode 100644 index 0000000..268acf7 --- /dev/null +++ b/well-known/oauth3/scopes.json @@ -0,0 +1,26 @@ +{ + + "oauth3_authn": "Basic secure authentication" + , "auth@oauth3.org": "Basic secure authentication" + , "wallet": "Access to payments and subscriptions" + , "bucket": "Access to file storage" + , "db": "Access to app data" + , "domains": "Domain registration (and Glue and NS records)" + , "domains@oauth3.org": "Domain registration (and Glue and NS records)" + , "domains:glue": "Glue Record management (for vanity nameservers)" + , "domains:ns": "Name Server management" + , "dns": "DNS records (A/AAAA, TXT, SRV, MX, etc)" + + , "hello@example.com": "Hello World Example Access" + , "authn@oauth3.org": "Basic secure authentication" + , "wallet@oauth3.org": "Access to payments and subscriptions" + , "bucket@oauth3.org": "Access to file storage" + , "db@oauth3.org": "Access to app data" + , "domains@oauth3.org": "Domain registration (and Glue and NS records)" + , "domains:glue@oauth3.org": "Glue Record management (for vanity nameservers)" + , "domains:ns@oauth3.org": "Name Server management" + , "dns@oauth3.org": "DNS records (A/AAAA, TXT, SRV, MX, etc)" + , "www@daplie.com": "Websites and webapps" + + , "*": "FULL ACCOUNT ACCESS" + } From d015e66f17f72a36750f2ecbb8d27b70d686e4fd Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 25 Nov 2017 07:46:37 +0000 Subject: [PATCH 4/5] WIP request rpc --- oauth3.core.js | 82 +++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index ea2611e..4d5e21b 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -294,25 +294,23 @@ } } , urls: { - , discover: function (providerUri, opts) { + , rpc: function (providerUri, opts) { if (!providerUri) { - throw new Error("cannot discover without providerUri"); + throw new Error("cannot run rpc without providerUri"); } if (!opts.client_id) { - throw new Error("cannot discover without options.client_id"); + throw new Error("cannot run rpc without options.client_id"); } var clientId = OAUTH3.url.normalize(opts.client_id || opts.client_uri); providerUri = OAUTH3.url.normalize(providerUri); - var discoverFile = opts.discoverFile || "directives.json"; var params = { - action: 'directives' //TODO: change this to not be directive specific. Is it even used? - , state: opts.state || OAUTH3.utils.randomState() + state: opts.state || OAUTH3.utils.randomState() , redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html#/') , response_type: 'rpc' - , discoverFile: opts.discoveFile || "directives.json" , _method: 'GET' - , _pathname: '.well-known/oauth3/directives.json' + , _scheme: opts._scheme + , _pathname: opts._pathname , debug: opts.debug || undefined }; @@ -325,6 +323,13 @@ return toRequest; } + , discover: function (providerUri, opts) { + return OAUTH3.urls.directives(providerUri, opts); + } + , directives: function (providerUri, opts) { + opts._pathname = ".well-known/oauth3/scopes.json"; + return OAUTH3.urls.rpc(providerUri, opts); + } , implicitGrant: function (directive, opts) { // // Example Implicit Grant Request @@ -669,21 +674,26 @@ } } , discoverScopes: function (providerUri, opts) { + return OAUTH.scopes(providerUri, opts); + } + , scopes: function (providerUri, opts) { if (!providerUri) { throw new Error('oauth3.discoverScopes(providerUri, opts) received providerUri as :', providerUri); } - var opts = opts || {}; - opts.discoverFile = "scopes.json"; + opts = opts || {}; + opts._pathname = ".well-known/oauth3/scopes.json"; //TODO: add caching - return OAUTH3._discoverHelper(providerUri, opts).then(function(scopes) { + return OAUTH3._rpcHelper(providerUri, opts).then(function(scopes) { return scopes; }); - } - + } , discover: function (providerUri, opts) { + return OAUTH3.directives(providerUri, opts); + } + , directives: function (providerUri, opts) { if (!providerUri) { throw new Error('oauth3.discover(providerUri, opts) received providerUri as :', providerUri); } @@ -693,7 +703,8 @@ return directives; } - return OAUTH3._discoverHelper(providerUri, opts).then(function (directives) { + opts._pathname = ".well-known/oauth3/directives.json"; + return OAUTH3._rpcHelper(providerUri, opts).then(function (directives) { directives.azp = directives.azp || OAUTH3.url.normalize(providerUri); directives.issuer = directives.issuer || OAUTH3.url.normalize(providerUri); directives.api = OAUTH3.url.normalize((directives.api||':hostname').replace(/:hostname/, OAUTH3.uri.normalize(directives.issuer) || OAUTH3.uri.normalize(providerUri))); @@ -702,9 +713,8 @@ }); }); } - , _discoverHelper: function(providerUri, opts) { - opts.discoverFile = "directives.json"; - return OAUTH3._browser.discover(providerUri, opts); + , _rpcHelper: function(providerUri, opts) { + return OAUTH3._browser.rpc(providerUri, opts); } , request: function (preq, opts) { function fetch() { @@ -884,28 +894,28 @@ // , _browser: { window: 'undefined' !== typeof window ? window : null - // TODO we don't need to include this if we're using jQuery or angular - , discover: function(providerUri, opts) { + , rpc: function(providerUri, opts) { opts = opts || {}; providerUri = OAUTH3.url.normalize(providerUri); - // If no discoverFile was specified, who knows what they want, but - // this function used to only support directives.json, so it's worth - // a shot. - var discoverFile = opts.discoverFile || "directives.json"; - + // TODO SECURITY should we whitelist our own self? if (OAUTH3.uri.normalize(providerUri).replace(/\/.*/, '') === OAUTH3.uri.normalize(OAUTH3._browser.window.location.hostname)) { - console.warn("It looks like you're a provider trying to discover on yourself," + console.warn("It looks like you're a provider trying to run rpc on yourself," + " so we we're just gonna use" + " OAUTH3.request({ method: 'GET', url: " - + "'/.well-known/oauth3/" + discoverFile + "' })"); + + "'" + opts._pathname + "' })"); - return OAUTH3.request({ - method: 'GET' - , url: OAUTH3.url.normalize(providerUri) + '/.well-known/oauth3/' + discoverFile - }).then(function (resp) { - return resp.data; - }); + if (/localstorage/i.test(opts._scheme)) { + return OAUTH3.PromiseA.resolve(localStorage.getItem(opts._pathname)); + } + else { + return OAUTH3.request({ + method: 'GET' + , url: OAUTH3.url.normalize(providerUri) + opts._pathname // '/.well-known/oauth3/' + discoverFile + }).then(function (resp) { + return resp.data; + }); + } } if (!(opts.client_id || opts.client_uri).match(OAUTH3._browser.window.location.hostname)) { @@ -914,18 +924,20 @@ console.warn(opts.client_id || opts.client_uri, OAUTH3._browser.window.location.hostname); } - var discReq = OAUTH3.urls.discover( + var discReq = OAUTH3.urls.rpc( providerUri , { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location)) , windowType: opts.broker && opts.windowType || 'background' , broker: opts.broker , state: opts._state || undefined , debug: opts.debug - , discoverFile: opts.discoverFile + , _scheme: opts._scheme + , _pathname: opts._pathname + , _method: opts._method } ); opts._state = discReq.state; - //var discReq = OAUTH3.urls.discover(providerUri, opts); + //var discReq = OAUTH3.urls.rpc(providerUri, opts); // hmm... we're gonna need a broker for this since switching windows is distracting, // popups are obnoxious, iframes are sometimes blocked, and most servers don't implement CORS From be9e8852b81833ec182bde3c2501d3785d674229 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Sat, 25 Nov 2017 08:09:57 +0000 Subject: [PATCH 5/5] WIP respond to RPC --- oauth3.core.js | 2 +- well-known/oauth3/index.html | 163 +++++++++++++++++++++-------------- 2 files changed, 99 insertions(+), 66 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index 4d5e21b..a4001f5 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -965,7 +965,7 @@ } // TODO params should have response_type indicating json, binary, etc - var result = JSON.parse(OAUTH3._base64.decodeUrlSafe(params.result || params.directives)); + var result = JSON.parse(OAUTH3._base64.decodeUrlSafe(params.data || params.result || params.directives)); // caller will call OAUTH3.hooks.directives.set(providerUri, directives); return result; }); diff --git a/well-known/oauth3/index.html b/well-known/oauth3/index.html index b31b6b9..a3d6c62 100644 --- a/well-known/oauth3/index.html +++ b/well-known/oauth3/index.html @@ -20,74 +20,20 @@ // TODO what about search within hash? var prefix = "(" + window.location.hostname + ") [.well-known/oauth3/]"; var params = OAUTH3.query.parse(window.location.hash || window.location.search); - if (params.debug) { - console.warn(prefix, "DEBUG MODE ENABLED. Automatic redirects disabled."); - } + var urlsafe64; + var redirect; + var err; + var oldRpc; + var sub = params.sub || params.subject; + var subData; - console.log(prefix, 'hash||search:'); - console.log(window.location.hash || window.location.search); - - console.log(prefix, 'params:'); - console.log(params); - - var fileWhiteList = [ - "directives.json" - , "scopes.json" ]; - - //Serving arbitrary files/paths is probably not a good idea. - //Let's make sure this is something we want to serve. - if(fileWhiteList.indexOf(params.discoverFile) === -1) { - //Nope! - var redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ - state: params.state - , error: "No access to requested file: " + params.discoverFile - , error_code: "E_ACCESS_DENIED" - , debug: params.debug || undefined - }); - - console.error(prefix, "Requested file is not listed as a discoverable file:" - , fileWhiteList); - console.log("Redirecting with error: ", redirect) - - if (!params.debug) { - window.location = redirect; - } else { - // yes, we're violating the security lint with purpose - document.body.innerHTML += window.location.host + window.location.pathname - + '

You\'ve passed the \'debug\' parameter so we\'re pausing' - + ' to let you look at logs or whatever it is that you intended to do.' - + '

The requested file was not a discoverable file (see console for details).' - + '

Continue with error redirect:
' + redirect + ''; + function doRedirect(redirect) { + if (params.debug) { + console.log(prefix, 'params.redirect_uri:', params.redirect_uri); + console.log(prefix, 'redirect'); + console.log(redirect); } - return; - } - OAUTH3.request({ url: params.discoverfile }).then(function (resp) { - var urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0)); - var redirect; - var returnParams; - - console.log(prefix, 'file contents'); - console.log(resp); - - console.log(prefix, 'base64'); - console.log(urlsafe64); - - // TODO try postMessage back to redirect_uri domain right here - // window.postMessage(); - - // TODO make sure it's https NOT http - // NOTE: this can be only up to 2,083 characters - console.log(prefix, 'params.redirect_uri:', params.redirect_uri); - redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ - state: params.state - , directives: urlsafe64 //kept for now, probably should remove this. - , result: urlsafe64 - , debug: params.debug || undefined - }) - - console.log(prefix, 'redirect'); - console.log(redirect); if (!params.debug) { window.location = redirect; } else { @@ -97,6 +43,93 @@ + ' to let you look at logs or whatever it is that you intended to do.' + '

Continue with redirect:
' + redirect + ''; } + } + + function onError(err) { + var redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ + state: params.state + , error: err.code + , error_description: err.message + , error_uri: err.uri + , debug: params.debug || undefined + }); + + doRedirect(redirect); + } + + function onSuccess(urlsafe64, hasSub) { + if (params.debug) { + console.log(prefix, 'directives'); + console.log(resp); + + console.log(prefix, 'base64'); + console.log(urlsafe64); + } + + // TODO try postMessage back to redirect_uri domain right here + // window.postMessage(); + + // TODO SECURITY make sure it's https NOT http + // NOTE: this can be only up to 2,083 characters + redirect = params.redirect_uri + '?' + OAUTH3.query.stringify({ + state: params.state + , directives: oldRpc ? urlsafe64 : undefined + , data: !oldRpc ? urlsafe64 : undefined + , sub: hasSub && sub || undefined + , debug: params.debug || undefined + }); + + doRedirect(redirect); + } + + if (params.debug) { + console.warn(prefix, "DEBUG MODE ENABLED. Automatic redirects disabled."); + + console.log(prefix, 'hash||search:'); + console.log(window.location.hash || window.location.search); + + console.log(prefix, 'params:'); + console.log(params); + } + + if ('rpc' !== params.response_type) { + err = new Error("response_type '" + params.response_type + "' is not supported"); + err.code = "E_RESPONSE_TYPE"; + // TODO err.uri + onError(err); + return; + } + + if (params.action) { + oldRpc = true; + } + + if (/localstorage/i.test(params._scheme)) { + if (sub) { + subData = localStorage.getItem(sub + '@oauth3.org:issuer'); + onSuccess(subData || localStorage.getItem('oauth3.org:issuer'), subData && true); + return; + } + onSuccess(localStorage.getItem('oauth3.org:issuer')); + return; + } + + var fileWhiteList = [ + '.well-known/oauth3/directives.json' + , '.well-known/oauth3/scopes.json' + ]; + + if (-1 === fileWhiteList.indexOf(params._pathname)) { + err = new Error("No access to requested file: " + params._pathname); + err.code = "E_ACCESS_DENIED" + // TODO err.uri + onError(err); + } + + OAUTH3.request({ url: 'directives.json' }).then(function (resp) { + urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0)); + + onSuccess(urlsafe64); }); }());