WIP window/iframe reuse
This commit is contained in:
parent
5a73cb1413
commit
4dd07c9f80
@ -22,6 +22,9 @@
|
||||
}
|
||||
, uri: {
|
||||
normalize: function (uri) {
|
||||
if ('string' !== typeof uri) {
|
||||
console.error((new Error('stack')).stack);
|
||||
}
|
||||
// tested with
|
||||
// example.com
|
||||
// example.com/
|
||||
@ -35,6 +38,9 @@
|
||||
}
|
||||
, url: {
|
||||
normalize: function (url) {
|
||||
if ('string' !== typeof url) {
|
||||
console.error((new Error('stack')).stack);
|
||||
}
|
||||
// tested with
|
||||
// example.com
|
||||
// example.com/
|
||||
@ -45,6 +51,15 @@
|
||||
.replace(/\/?$/, '')
|
||||
;
|
||||
}
|
||||
, resolve: function (base, next) {
|
||||
if (/^https:\/\//i.test(next)) {
|
||||
return next;
|
||||
}
|
||||
return this.normalize(base) + '/' + this._normalizePath(next);
|
||||
}
|
||||
, _normalizePath: function (path) {
|
||||
return path.replace(/^\//, '').replace(/\/$/, '');
|
||||
}
|
||||
}
|
||||
, query: {
|
||||
stringify: function (params) {
|
||||
@ -115,7 +130,7 @@
|
||||
|
||||
var params = {
|
||||
action: 'directives'
|
||||
, 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'
|
||||
, _method: 'GET'
|
||||
@ -130,24 +145,95 @@
|
||||
, query: params
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
, implicitGrant: function (directive, opts) {
|
||||
//console.log('[implicitGrant]');
|
||||
//
|
||||
// Example Implicit Grant Request
|
||||
// (for generating a browser-only session, not a session on your server)
|
||||
//
|
||||
// GET https://example.com/api/org.oauth3.provider/authorization_dialog
|
||||
// ?response_type=token
|
||||
// &scope=`encodeURIComponent('profile.login profile.email')`
|
||||
// &state=`cryptoutil.random().toString('hex')`
|
||||
// &client_id=xxxxxxxxxxx
|
||||
// &redirect_uri=`encodeURIComponent('https://myapp.com/oauth3.html')`
|
||||
//
|
||||
// NOTE: `redirect_uri` itself may also contain URI-encoded components
|
||||
//
|
||||
|
||||
opts = opts || {};
|
||||
var type = 'authorization_dialog';
|
||||
var responseType = 'token';
|
||||
|
||||
var scope = opts.scope || directive.authn_scope;
|
||||
var args = directive[type];
|
||||
var uri = args.url;
|
||||
var state = opts.state || OAUTH3.utils.randomState();
|
||||
console.log('implicit grant opts.state', opts.state);
|
||||
var params = {
|
||||
debug: opts.debug || undefined
|
||||
, client_uri: opts.client_uri || opts.clientUri || undefined
|
||||
, client_id: opts.client_id || opts.client_uri || undefined
|
||||
};
|
||||
var result;
|
||||
|
||||
params.state = state;
|
||||
params.response_type = responseType;
|
||||
if (scope) {
|
||||
params.scope = OAUTH3.utils.scope.stringify(scope);
|
||||
}
|
||||
if (!opts.redirect_uri) {
|
||||
// TODO consider making this optional
|
||||
console.warn("auto-generating redirect_uri from hard-coded callback.html"
|
||||
+ " (should be configurable... but then redirect_uri could just be manually-generated)");
|
||||
opts.redirect_uri = OAUTH3.utils.url.resolve(
|
||||
OAUTH3.utils.url.normalize(params.client_uri)
|
||||
, '.well-known/oauth3/callback.html'
|
||||
);
|
||||
}
|
||||
params.redirect_uri = opts.redirect_uri;
|
||||
|
||||
uri += '?' + OAUTH3.utils.query.stringify(params);
|
||||
|
||||
result = {
|
||||
url: uri
|
||||
, state: state
|
||||
, method: args.method
|
||||
, query: params
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
, hooks: {
|
||||
directives: {
|
||||
get: function (providerUri) {
|
||||
_get: function (providerUri) {
|
||||
providerUri = OAUTH3.utils.uri.normalize(providerUri);
|
||||
console.warn('[Warn] You should implement: OAUTH3.hooks.directives.get = function (providerUri) { return directives; }');
|
||||
if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._cache[providerUri] || this.get(providerUri))
|
||||
.then(function (directives) {
|
||||
// or do .then(this._set) to keep DRY?
|
||||
OAUTH3.hooks.directives._cache[providerUri] = directives;
|
||||
});
|
||||
}
|
||||
, _getCached: function (providerUri) {
|
||||
return OAUTH3.hooks.directives._cache[providerUri];
|
||||
}
|
||||
, get: function (providerUri) {
|
||||
console.warn('[Warn] You should implement: OAUTH3.hooks.directives.get = function (providerUri) { return directives; }');
|
||||
return JSON.parse(window.localStorage.getItem('directives-' + providerUri) || '{}');
|
||||
}
|
||||
, set: function (providerUri, directives) {
|
||||
, _set: function (providerUri, directives) {
|
||||
providerUri = OAUTH3.utils.uri.normalize(providerUri);
|
||||
console.warn('[Warn] You should implement: OAUTH3.hooks.directives.set = function (providerUri, directives) { return directives; }');
|
||||
console.warn(directives);
|
||||
if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
|
||||
window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives));
|
||||
OAUTH3.hooks.directives._cache[providerUri] = directives;
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives.set(providerUri, directives));
|
||||
}
|
||||
, set: function (providerUri, directives) {
|
||||
console.warn('[Warn] You should implement: OAUTH3.hooks.directives.set = function (providerUri, directives) { return directives; }');
|
||||
window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives));
|
||||
return directives;
|
||||
}
|
||||
}
|
||||
@ -161,17 +247,13 @@
|
||||
if (directives && directives.issuer) {
|
||||
return directives;
|
||||
}
|
||||
return OAUTH3._discoverHelper(providerUri, opts).then(function (directives) {
|
||||
return OAUTH3._discover(providerUri, opts).then(function (directives) {
|
||||
directives.issuer = directives.issuer || OAUTH3.utils.url.normalize(providerUri);
|
||||
// OAUTH3.PromiseA.resolve() is taken care of because this is wrapped
|
||||
return OAUTH3.hooks.directives.set(providerUri, directives);
|
||||
});
|
||||
});
|
||||
}
|
||||
// this is the browser version
|
||||
, _discoverHelper: function (providerUri, opts) {
|
||||
return OAUTH3._browser.discover(providerUri, opts);
|
||||
}
|
||||
, request: function (preq) {
|
||||
return OAUTH3._browser.request(preq);
|
||||
}
|
||||
@ -179,10 +261,15 @@
|
||||
var promise;
|
||||
|
||||
if (opts.broker) {
|
||||
// Discovery can happen in-flow because we know that this is
|
||||
// a valid oauth3 provider
|
||||
console.info("broker implicit grant");
|
||||
promise = OAUTH3._discoverThenImplicitGrant(providerUri, opts);
|
||||
}
|
||||
else {
|
||||
promise = OAUTH3._implicitGrant(providerUri, opts);
|
||||
// Discovery must take place before calling implicitGrant
|
||||
console.info("direct implicit grant");
|
||||
promise = OAUTH3._implicitGrant(OAUTH3.hooks.directives._getCached(providerUri), opts);
|
||||
}
|
||||
|
||||
return promise.then(function (tokens) {
|
||||
@ -199,9 +286,14 @@
|
||||
, _discoverThenImplicitGrant: function(providerUri, opts) {
|
||||
opts.windowType = opts.windowType || 'popup';
|
||||
return OAUTH3._discover(providerUri, opts).then(function (directives) {
|
||||
console.info('discover complete');
|
||||
console.log(directives);
|
||||
console.log('DISCOVER COMPLETE opts._state', opts._state);
|
||||
return OAUTH3._implicitGrant(directives, opts).then(function (tokens) {
|
||||
console.info('implicit grant complete', tokens);
|
||||
OAUTH3._browser.closeFrame(tokens.state || opts._state);
|
||||
opts._state = undefined;
|
||||
//opts._state = undefined;
|
||||
return tokens;
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -229,6 +321,7 @@
|
||||
, { 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
|
||||
}
|
||||
);
|
||||
@ -242,7 +335,7 @@
|
||||
// TODO allow postMessage from providerUri in addition to callback
|
||||
// TODO allow node to open a desktop browser window
|
||||
return OAUTH3._browser.frameRequest(
|
||||
discReq.url
|
||||
OAUTH3.utils.url.resolve(providerUri, discReq.url)
|
||||
, discReq.state
|
||||
, { windowType: opts.windowType
|
||||
, reuseWindow: opts.broker && '-broker'
|
||||
@ -255,16 +348,15 @@
|
||||
return OAUTH3.hooks.directives.set(providerUri, directives);
|
||||
});
|
||||
}
|
||||
, _implicitGrant: function(providerUri, opts) {
|
||||
, _implicitGrant: function(directives, opts) {
|
||||
// TODO this may need to be synchronous for browser security policy
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives.get(providerUri)).then(function (directives) {
|
||||
// Do some stuff
|
||||
var authReq = OAUTH3.urls.implicitGrant(
|
||||
directives
|
||||
, { redirect_uri: opts.redirect_uri
|
||||
, client_id: opts.client_id || opts.client_uri
|
||||
, client_uri: opts.client_uri || opts.client_id
|
||||
, state: opts._state
|
||||
, state: opts._state || undefined
|
||||
, debug: opts.debug
|
||||
}
|
||||
);
|
||||
@ -273,17 +365,26 @@
|
||||
window.alert("DEBUG MODE: Pausing so you can look at logs and whatnot :) Fire at will!");
|
||||
}
|
||||
|
||||
if (opts._state) {
|
||||
console.log('equal states authReq?', authReq.state === opts._state);
|
||||
console.log(opts._state);
|
||||
console.log(authReq.state);
|
||||
}
|
||||
|
||||
return new OAUTH3.PromiseA(function (resolve, reject) {
|
||||
console.log("framing request for implicit grant");
|
||||
return OAUTH3._browser.frameRequest(
|
||||
authReq.url
|
||||
OAUTH3.utils.url.resolve(directives.issuer, authReq.url)
|
||||
, authReq.state // state should recycle params
|
||||
, { windowType: opts.windowType
|
||||
, reuseWindow: opts.broker && '-broker'
|
||||
, debug: opts.debug
|
||||
}
|
||||
).then(function (tokens) {
|
||||
console.log("completed implicit grant");
|
||||
if (tokens.error) {
|
||||
return reject(OAUTH3.utils._formatError(tokens.error));
|
||||
// TODO directives.audience
|
||||
return reject(OAUTH3.utils._formatError(directives.issuer /*providerUri*/, tokens));
|
||||
}
|
||||
|
||||
OAUTH3._browser.closeFrame(authReq.state, { debug: opts.debug || tokens.debug });
|
||||
@ -291,7 +392,6 @@
|
||||
return tokens;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -358,28 +458,40 @@
|
||||
console.warn("It looks like your client_id doesn't match your current window... this probably won't end well");
|
||||
console.warn(opts.client_id || opts.client_uri, OAUTH3._browser.window.location.hostname);
|
||||
}
|
||||
var discObj = OAUTH3.urls.discover(
|
||||
var discReq = OAUTH3.urls.discover(
|
||||
providerUri
|
||||
, { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location)), debug: opts.debug }
|
||||
, { client_id: (opts.client_id || opts.client_uri || OAUTH3.clientUri(OAUTH3._browser.window.location))
|
||||
, state: opts._state || undefined
|
||||
, debug: opts.debug }
|
||||
);
|
||||
|
||||
if (opts._state) {
|
||||
console.log('equal states discObj?', discReq.state === opts._state);
|
||||
}
|
||||
|
||||
// TODO ability to reuse iframe instead of closing
|
||||
opts._windowType = opts.windowType;
|
||||
opts.windowType = opts.windowType || 'background';
|
||||
return OAUTH3._browser.frameRequest(discObj.url, discObj.state, opts).then(function (params) {
|
||||
return OAUTH3._browser.frameRequest(
|
||||
OAUTH3.utils.url.resolve(providerUri, discReq.url)
|
||||
, discReq.state
|
||||
, opts
|
||||
).then(function (params) {
|
||||
opts.windowType = opts._windowType;
|
||||
OAUTH3._browser.closeFrame(discObj.state, { debug: opts.debug || params.debug });
|
||||
OAUTH3._browser.closeFrame(discReq.state, { debug: opts.debug || params.debug });
|
||||
if (params.error) {
|
||||
return OAUTH3.utils._formatError(providerUri, params.error);
|
||||
// TODO directives.issuer || directives.audience
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(providerUri, params));
|
||||
}
|
||||
var directives = JSON.parse(OAUTH3.utils.atob(OAUTH3.utils._urlSafeBase64ToBase64(params.result || params.directives)));
|
||||
return directives;
|
||||
}, function (err) {
|
||||
OAUTH3._browser.closeFrame(discObj.state, { debug: opts.debug || err.debug });
|
||||
OAUTH3._browser.closeFrame(discReq.state, { debug: opts.debug || err.debug });
|
||||
return OAUTH3.PromiseA.reject(err);
|
||||
});
|
||||
}
|
||||
, frameRequest: function (url, state, opts) {
|
||||
console.log('frameRequest state', state);
|
||||
opts = opts || {};
|
||||
var previousFrame = OAUTH3._browser._frames[state];
|
||||
|
||||
@ -412,6 +524,8 @@
|
||||
|
||||
console.log('[oauth3.implicit.js] callbackName', '--oauth3-callback-' + state);
|
||||
window['--oauth3-callback-' + state] = function (params) {
|
||||
console.log("YO HO YO HO, A Pirate's life for me!", state);
|
||||
console.error(new Error("Pirate's Life").stack);
|
||||
resolve(params);
|
||||
cleanup();
|
||||
};
|
||||
@ -434,20 +548,26 @@
|
||||
|
||||
if ('background' === windowType) {
|
||||
if (previousFrame) {
|
||||
console.log('previous frame in background');
|
||||
previousFrame.location = url;
|
||||
//promise = previousFrame.promise;
|
||||
}
|
||||
else {
|
||||
console.log('NO previous frame in background');
|
||||
OAUTH3._browser._frames[state] = OAUTH3._browser.iframe(url, state, opts);
|
||||
}
|
||||
} else if ('popup' === windowType) {
|
||||
if (previousFrame) {
|
||||
console.log('previous frame in pop');
|
||||
console.log(previousFrame);
|
||||
console.log(url);
|
||||
previousFrame.location = url;
|
||||
if (opts.debug) {
|
||||
previousFrame.focus();
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('NO previous frame in popup');
|
||||
OAUTH3._browser._frames[state] = OAUTH3._browser.frame(url, state, opts);
|
||||
}
|
||||
} else if ('inline' === windowType) {
|
||||
@ -463,27 +583,25 @@
|
||||
}
|
||||
|
||||
}).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;
|
||||
//_formatError
|
||||
return OAUTH3.PromiseA.reject(err);
|
||||
console.log('frameRequest formatting params (weird that this place exists, but not weird to be here)');
|
||||
if (params.error) {
|
||||
// TODO directives.issuer || directives.audience
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError('https://oauth3.org', params));
|
||||
}
|
||||
|
||||
return params;
|
||||
});
|
||||
}
|
||||
, closeFrame: function (state, opts) {
|
||||
function close() {
|
||||
console.log("Attempting to close... ", OAUTH3._browser._frames[state]);
|
||||
try {
|
||||
OAUTH3._browser._frames[state].close();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
try {
|
||||
OAUTH3._browser._frames[state].remove();
|
||||
} catch(e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user