merge rpc

This commit is contained in:
AJ ONeal 2017-11-25 08:15:44 +00:00
commit 9f48a44958
3 changed files with 199 additions and 60 deletions

View File

@ -1,4 +1,4 @@
/* global Promise */ / * global Promise */
;(function (exports) { ;(function (exports) {
'use strict'; 'use strict';
@ -294,34 +294,41 @@
} }
} }
, urls: { , urls: {
discover: function (providerUri, opts) { , rpc: function (providerUri, opts) {
if (!providerUri) { if (!providerUri) {
throw new Error("cannot discover without providerUri"); throw new Error("cannot run rpc without providerUri");
} }
if (!opts.client_id) { 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); var clientId = OAUTH3.url.normalize(opts.client_id || opts.client_uri);
providerUri = OAUTH3.url.normalize(providerUri); providerUri = OAUTH3.url.normalize(providerUri);
var params = { var params = {
action: 'directives' state: opts.state || OAUTH3.utils.randomState()
, state: opts.state || OAUTH3.utils.randomState()
, redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html#/') , redirect_uri: clientId + (opts.client_callback_path || '/.well-known/oauth3/callback.html#/')
, response_type: 'rpc' , response_type: 'rpc'
, _method: 'GET' , _method: 'GET'
, _pathname: '.well-known/oauth3/directives.json' , _scheme: opts._scheme
, _pathname: opts._pathname
, debug: opts.debug || undefined , debug: opts.debug || undefined
}; };
var result = { var toRequest = {
url: providerUri + '/.well-known/oauth3/#/?' + OAUTH3.query.stringify(params) url: providerUri + '/.well-known/oauth3/#/?' + OAUTH3.query.stringify(params)
, state: params.state , state: params.state
, method: 'GET' , method: 'GET'
, query: params , query: params
}; };
return result; 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) { , implicitGrant: function (directive, opts) {
// //
@ -530,6 +537,14 @@
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.clear()); return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.clear());
} }
} }
, scopes: {
get: function(providerUri) {
//TODO: retrieve cached scopes
}
, set: function(providerUri, scopes) {
//TODO: cache scopes
}
}
, session: { , session: {
refresh: function (oldSession, newSession) { refresh: function (oldSession, newSession) {
var providerUri = oldSession.provider_uri; var providerUri = oldSession.provider_uri;
@ -658,9 +673,29 @@
} }
} }
} }
, discover: function (providerUri, opts) { , discoverScopes: function (providerUri, opts) {
return OAUTH.scopes(providerUri, opts);
}
, scopes: function (providerUri, opts) {
if (!providerUri) { if (!providerUri) {
throw new Error('oauth3.discover(providerUri, opts) received providerUri as ' + providerUri); throw new Error('oauth3.discoverScopes(providerUri, opts) received providerUri as :', providerUri);
}
opts = opts || {};
opts._pathname = ".well-known/oauth3/scopes.json";
//TODO: add caching
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);
} }
return OAUTH3.hooks.directives.get(providerUri).then(function (directives) { return OAUTH3.hooks.directives.get(providerUri).then(function (directives) {
@ -668,7 +703,8 @@
return directives; 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.azp = directives.azp || OAUTH3.url.normalize(providerUri);
directives.issuer = directives.issuer || 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))); directives.api = OAUTH3.url.normalize((directives.api||':hostname').replace(/:hostname/, OAUTH3.uri.normalize(directives.issuer) || OAUTH3.uri.normalize(providerUri)));
@ -677,8 +713,8 @@
}); });
}); });
} }
, _discoverHelper: function(providerUri, opts) { , _rpcHelper: function(providerUri, opts) {
return OAUTH3._browser.discover(providerUri, opts); return OAUTH3._browser.rpc(providerUri, opts);
} }
, request: function (preq, opts) { , request: function (preq, opts) {
function fetch() { function fetch() {
@ -858,21 +894,28 @@
// //
, _browser: { , _browser: {
window: 'undefined' !== typeof window ? window : null window: 'undefined' !== typeof window ? window : null
// TODO we don't need to include this if we're using jQuery or angular , rpc: function(providerUri, opts) {
, discover: function(providerUri, opts) {
opts = opts || {}; opts = opts || {};
providerUri = OAUTH3.url.normalize(providerUri); providerUri = OAUTH3.url.normalize(providerUri);
// TODO SECURITY should we whitelist our own self?
if (OAUTH3.uri.normalize(providerUri).replace(/\/.*/, '') === OAUTH3.uri.normalize(OAUTH3._browser.window.location.hostname)) { 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 run rpc on yourself,"
+ " so we we're just gonna use" + " so we we're just gonna use"
+ " OAUTH3.request({ method: 'GET', url: '.well-known/oauth3/directive.json' })"); + " OAUTH3.request({ method: 'GET', url: "
return OAUTH3.request({ + "'" + opts._pathname + "' })");
method: 'GET'
, url: OAUTH3.url.normalize(providerUri) + '/.well-known/oauth3/directives.json' if (/localstorage/i.test(opts._scheme)) {
}).then(function (resp) { return OAUTH3.PromiseA.resolve(localStorage.getItem(opts._pathname));
return resp.data; }
}); 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)) { if (!(opts.client_id || opts.client_uri).match(OAUTH3._browser.window.location.hostname)) {
@ -881,17 +924,20 @@
console.warn(opts.client_id || opts.client_uri, OAUTH3._browser.window.location.hostname); console.warn(opts.client_id || opts.client_uri, OAUTH3._browser.window.location.hostname);
} }
var discReq = OAUTH3.urls.discover( var discReq = OAUTH3.urls.rpc(
providerUri providerUri
, { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location)) , { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location))
, windowType: opts.broker && opts.windowType || 'background' , windowType: opts.broker && opts.windowType || 'background'
, broker: opts.broker , broker: opts.broker
, state: opts._state || undefined , state: opts._state || undefined
, debug: opts.debug , debug: opts.debug
, _scheme: opts._scheme
, _pathname: opts._pathname
, _method: opts._method
} }
); );
opts._state = discReq.state; 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, // 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 // popups are obnoxious, iframes are sometimes blocked, and most servers don't implement CORS
@ -920,9 +966,9 @@
} }
// TODO params should have response_type indicating json, binary, etc // 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.data || params.result || params.directives));
// caller will call OAUTH3.hooks.directives.set(providerUri, directives); // caller will call OAUTH3.hooks.directives.set(providerUri, directives);
return directives; return result;
}); });
}); });
} }

View File

@ -20,40 +20,20 @@
// TODO what about search within hash? // TODO what about search within hash?
var prefix = "(" + window.location.hostname + ") [.well-known/oauth3/]"; var prefix = "(" + window.location.hostname + ") [.well-known/oauth3/]";
var params = OAUTH3.query.parse(window.location.hash || window.location.search); var params = OAUTH3.query.parse(window.location.hash || window.location.search);
if (params.debug) { var urlsafe64;
console.warn(prefix, "DEBUG MODE ENABLED. Automatic redirects disabled."); var redirect;
} var err;
var oldRpc;
var sub = params.sub || params.subject;
var subData;
console.log(prefix, 'hash||search:'); function doRedirect(redirect) {
console.log(window.location.hash || window.location.search); if (params.debug) {
console.log(prefix, 'params.redirect_uri:', params.redirect_uri);
console.log(prefix, 'redirect');
console.log(redirect);
}
console.log(prefix, 'params:');
console.log(params);
OAUTH3.request({ url: 'directives.json' }).then(function (resp) {
var urlsafe64 = OAUTH3._base64.encodeUrlSafe(JSON.stringify(resp.data, null, 0));
var redirect;
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 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
, debug: params.debug || undefined
})
console.log(prefix, 'redirect');
console.log(redirect);
if (!params.debug) { if (!params.debug) {
window.location = redirect; window.location = redirect;
} else { } else {
@ -63,6 +43,93 @@
+ ' to let you look at logs or whatever it is that you intended to do.' + ' to let you look at logs or whatever it is that you intended to do.'
+ '<br/><br/>Continue with redirect: <a href="' + redirect + '">' + redirect + '</' + 'a>'; + '<br/><br/>Continue with redirect: <a href="' + redirect + '">' + redirect + '</' + 'a>';
} }
}
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);
}); });
}()); }());

View File

@ -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"
}