seems to work...

This commit is contained in:
AJ ONeal 2017-02-14 14:37:04 -07:00
parent ab4dfdce58
commit 69a92fc2fd
1 changed files with 302 additions and 46 deletions

View File

@ -7,6 +7,12 @@
clientUri: function (location) {
return OAUTH3.utils.uri.normalize(location.host + location.pathname);
}
, _formatError: function (providerUri, params) {
var err = new Error(params.error_description || params.error.message || "Unknown error with provider '" + providerUri + "'");
err.uri = params.error_uri || params.error.uri;
err.code = params.error.code || params.error;
return err;
}
, atob: function (base64) {
return (exports.atob || require('atob'))(base64);
}
@ -117,6 +123,59 @@
return str;
}
}
, jwt: {
// decode only (no verification)
decode: function (str) {
// 'abc.qrs.xyz'
// [ 'abc', 'qrs', 'xyz' ]
// [ {}, {}, 'foo' ]
// { header: {}, payload: {}, signature: }
var parts = str.split(/\./g);
var jsons = parts.slice(0, 2).map(function (urlsafe64) {
var atob = exports.atob || require('atob');
var b64 = OAUTH3.utils._urlSafeBase64ToBase64(urlsafe64);
return atob(b64);
});
return {
header: JSON.parse(jsons[0])
, payload: JSON.parse(jsons[1])
, signature: parts[2] // should remain url-safe base64
};
}
, getFreshness: function (tokenMeta, staletime, now) {
staletime = staletime || (15 * 60);
now = now || Date.now();
var fresh = ((parseInt(tokenMeta.exp, 10) || 0) - Math.round(now / 1000));
if (fresh >= staletime) {
return 'fresh';
}
if (fresh <= 0) {
return 'expired';
}
return 'stale';
}
/*
// encode-only (no signature)
, encode: function (parts) {
parts.header = parts.header || { alg: 'none', typ: 'jwt' };
parts.signature = parts.signature || '';
var btoa = exports.btoa || require('btoa');
var result = [
core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.header, null)))
, core.utils.base64ToUrlSafeBase64(btoa(JSON.stringify(parts.payload, null)))
, parts.signature // should already be url-safe base64
].join('.');
return result;
}
*/
}
, urls: {
discover: function (providerUri, opts) {
if (!providerUri) {
@ -205,6 +264,58 @@
return result;
}
, refreshToken: function (directive, opts) {
// grant_type=refresh_token
// Example Refresh Token Request
// (generally for 1st or 3rd party server-side, mobile, and desktop apps)
//
// POST https://example.com/api/oauth3/access_token
// { "grant_type": "refresh_token", "client_id": "<<id>>", "scope": "<<scope>>"
// , "username": "<<username>>", "password": "password" }
//
opts = opts || {};
var type = 'access_token';
var grantType = 'refresh_token';
var scope = opts.scope || directive.authn_scope;
var clientSecret = opts.client_secret;
var args = directive[type];
var params = {
"grant_type": grantType
, "refresh_token": opts.refresh_token || (opts.session && opts.session.refresh_token)
, "response_type": 'token'
, "client_id": opts.client_id || opts.client_uri
, "client_uri": opts.client_uri
//, "scope": undefined
//, "client_secret": undefined
, debug: opts.debug || undefined
};
var uri = args.url;
var body;
if (clientSecret) {
// TODO not allowed in the browser
console.warn("if this is a browser, you must not use client_secret");
params.client_secret = clientSecret;
}
if (scope) {
params.scope = OAUTH3.utils.scope.stringify(scope);
}
if ('GET' === args.method.toUpperCase()) {
uri += '?' + OAUTH3.utils.query.stringify(params);
} else {
body = params;
}
return {
url: uri
, method: args.method
, data: body
};
}
}
, hooks: {
directives: {
@ -236,6 +347,118 @@
return directives;
}
}
, session: {
refresh: function (oldSession, newSession) {
var providerUri = oldSession.provider_uri;
var clientUri = oldSession.client_uri;
console.info('[oauth3.hooks.refreshSession] oldSession', JSON.parse(JSON.stringify(oldSession)));
console.info('[oauth3.hooks.refreshSession] newSession', newSession);
Object.keys(oldSession).forEach(function (key) {
oldSession[key] = undefined;
});
Object.keys(newSession).forEach(function (key) {
oldSession[key] = newSession[key];
});
// info about the session of this API call
oldSession.provider_uri = providerUri; // aud
oldSession.client_uri = clientUri; // azp
// info about the newly-discovered token
oldSession.token = OAUTH3.jwt.decode(oldSession.access_token).payload;
oldSession.token.sub = oldSession.token.sub || oldSession.token.acx.id;
oldSession.token.client_uri = clientUri;
oldSession.token.provider_uri = providerUri;
if (oldSession.refresh_token) {
oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token).payload;
oldSession.refresh.sub = oldSession.refresh.sub || oldSession.refresh.acx.id;
oldSession.refresh.provider_uri = providerUri;
}
console.info('[oauth3.hooks.refreshSession] refreshedSession', oldSession);
// set for a set of audiences
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session.set(providerUri, oldSession));
}
, check: function (preq, opts) {
if (!preq.session) {
console.warn('[oauth3.hooks.checkSession] no session');
return OAUTH3.PromiseA.resolve(null);
}
var freshness = OAUTH3.jwt.freshness(preq.session.token, opts.staletime);
console.info('[oauth3.hooks.checkSession] freshness', freshness, preq.session);
switch (freshness) {
case 'stale':
return OAUTH3.hooks.session.stale(preq.session);
case 'expired':
return OAUTH3.hooks.session.expired(preq.session).then(function (newSession) {
preq.session = newSession;
return newSession;
});
//case 'fresh':
default:
return OAUTH3.PromiseA.resolve(preq.session);
}
}
, stale: function (staleSession) {
console.info('[oauth3.hooks.sessionStale] called');
if (OAUTH3.hooks.session._stalePromise) {
return OAUTH3.PromiseA.resolve(staleSession);
}
OAUTH3.hooks.session._stalePromise = OAUTH3._refreshToken(
staleSession.provider_uri
, { client_uri: staleSession.client_uri
, session: staleSession
, debug: staleSession.debug
}
).then(function (newSession) {
OAUTH3.hooks.session._stalePromise = null;
return newSession; // oauth3.hooks.refreshSession(staleSession, newSession);
}, function () {
OAUTH3.hooks.session._stalePromise = null;
});
return OAUTH3.PromiseA.resolve(staleSession);
}
, expired: function (expiredSession) {
console.info('[oauth3.hooks.sessionExpired] called');
return OAUTH3._refreshToken(
expiredSession.provider_uri
, { client_uri: expiredSession.client_uri
, session: expiredSession
, debug: expiredSession.debug
}
).then(function (newSession) {
return newSession; // oauth3.hooks.refreshSession(expiredSession, newSession);
});
}
, set: function (providerUri, newSession) {
if (!providerUri) {
console.error(new Error('no providerUri').stack);
throw new Error("providerUri is not set");
}
providerUri = OAUTH3.utils.uri.normalize(providerUri);
console.warn('[Warn] Please implement OAUTH3.hooks.session.set = function (providerUri, newSession) { return PromiseA<newSession>; }');
console.warn(newSession);
if (!OAUTH3.hooks.session._sessions) { OAUTH3.hooks.session._sessions = {}; }
OAUTH3.hooks.session._sessions[providerUri] = newSession;
return OAUTH3.PromiseA.resolve(newSession);
}
, get: function (providerUri) {
providerUri = OAUTH3.utils.uri.normalize(providerUri);
if (!providerUri) {
throw new Error("providerUri is not set");
}
console.warn('[Warn] Please implement OAUTH3.hooks.session.get = function (providerUri) { return PromiseA<savedSession>; }');
if (!OAUTH3.hooks.session._sessions) { OAUTH3.hooks.session._sessions = {}; }
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session._sessions[providerUri]);
}
}
}
, discover: function (providerUri, opts) {
if (!providerUri) {
@ -253,47 +476,6 @@
});
});
}
, request: function (preq) {
return OAUTH3._browser.request(preq);
}
, implicitGrant: function(providerUri, opts) {
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 {
// 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) {
return OAUTH3.hooks.refreshSession(
opts.session || {
provider_uri: providerUri
, client_id: opts.client_id
, client_uri: opts.client_uri || opts.clientUri
}
, tokens
);
});
}
, _discoverThenImplicitGrant: function(providerUri, opts) {
opts.windowType = opts.windowType || 'popup';
return OAUTH3.discover(providerUri, opts).then(function (directives) {
console.info('Discover complete');
return OAUTH3._implicitGrant(directives, opts).then(function (tokens) {
console.info('Implicit Grant complete', tokens);
OAUTH3._browser.closeFrame(tokens.state || opts._state);
//opts._state = undefined;
return tokens;
});
});
}
, _discoverHelper: function(providerUri, opts) {
return OAUTH3._discover(providerUri, opts);
}
@ -360,6 +542,70 @@
return directives;
});
}
, request: function (preq, opts) {
function fetch() {
if (preq.session) {
// TODO check session.token.aud against preq.url to make sure they match
console.warn("[security] session audience checking has not been implemented yet (it's up to you to check)");
preq.headers = preq.headers || {};
preq.headers.Authorization = 'Bearer ' + (preq.session.access_token || preq.session.accessToken);
}
return OAUTH3._requestHelper(preq, opts);
}
if (!preq.session) {
return fetch();
}
return OAUTH3.hooks.session.check(preq, opts).then(fetch);
}
, _requestHelper: function (preq, opts) {
return OAUTH3._browser.request(preq, opts);
}
, implicitGrant: function(providerUri, opts) {
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 {
// 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) {
// TODO abstract browser bits away
try {
OAUTH3._browser.closeFrame(tokens.state || opts._state, opts);
} catch(e) {
console.warn("[implicitGrant] TODO abstract browser bits away");
}
opts._state = undefined;
return OAUTH3.hooks.session.refresh(
opts.session || {
provider_uri: providerUri
, client_id: opts.client_id
, client_uri: opts.client_uri || opts.clientUri
}
, tokens
);
});
}
, _discoverThenImplicitGrant: function(providerUri, opts) {
opts.windowType = opts.windowType || 'popup';
return OAUTH3.discover(providerUri, opts).then(function (directives) {
console.info('Discover complete');
return OAUTH3._implicitGrant(directives, opts).then(function (tokens) {
console.info('Implicit Grant complete', tokens);
return tokens;
});
});
}
, _implicitGrant: function(directives, opts) {
// TODO this may need to be synchronous for browser security policy
// Do some stuff
@ -392,11 +638,24 @@
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(directives.issuer /*providerUri*/, tokens));
}
OAUTH3._browser.closeFrame(authReq.state, { debug: opts.debug || tokens.debug });
return tokens;
});
}
, _refreshToken: function (providerUri, opts) {
console.info('[oauth3.requests.refreshToken] called', providerUri, opts);
return OAUTH3.discover(providerUri, opts).then(function (directive) {
var prequest = OAUTH3.urls.refreshToken(directive, opts);
return OAUTH3.request(prequest).then(function (req) {
var data = req.data;
data.provider_uri = providerUri;
if (data.error) {
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(providerUri, data));
}
return OAUTH3.hooks.session.refresh(opts, data);
});
});
}
//
@ -477,10 +736,7 @@
}
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();
};