seems to work...
This commit is contained in:
parent
ab4dfdce58
commit
69a92fc2fd
|
@ -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();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue