seems to work...
This commit is contained in:
parent
ab4dfdce58
commit
69a92fc2fd
@ -7,6 +7,12 @@
|
|||||||
clientUri: function (location) {
|
clientUri: function (location) {
|
||||||
return OAUTH3.utils.uri.normalize(location.host + location.pathname);
|
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) {
|
, atob: function (base64) {
|
||||||
return (exports.atob || require('atob'))(base64);
|
return (exports.atob || require('atob'))(base64);
|
||||||
}
|
}
|
||||||
@ -117,6 +123,59 @@
|
|||||||
return str;
|
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: {
|
, urls: {
|
||||||
discover: function (providerUri, opts) {
|
discover: function (providerUri, opts) {
|
||||||
if (!providerUri) {
|
if (!providerUri) {
|
||||||
@ -205,6 +264,58 @@
|
|||||||
|
|
||||||
return result;
|
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: {
|
, hooks: {
|
||||||
directives: {
|
directives: {
|
||||||
@ -236,6 +347,118 @@
|
|||||||
return directives;
|
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) {
|
, discover: function (providerUri, opts) {
|
||||||
if (!providerUri) {
|
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) {
|
, _discoverHelper: function(providerUri, opts) {
|
||||||
return OAUTH3._discover(providerUri, opts);
|
return OAUTH3._discover(providerUri, opts);
|
||||||
}
|
}
|
||||||
@ -360,6 +542,70 @@
|
|||||||
return directives;
|
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) {
|
, _implicitGrant: function(directives, opts) {
|
||||||
// TODO this may need to be synchronous for browser security policy
|
// TODO this may need to be synchronous for browser security policy
|
||||||
// Do some stuff
|
// Do some stuff
|
||||||
@ -392,11 +638,24 @@
|
|||||||
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(directives.issuer /*providerUri*/, tokens));
|
return OAUTH3.PromiseA.reject(OAUTH3.utils._formatError(directives.issuer /*providerUri*/, tokens));
|
||||||
}
|
}
|
||||||
|
|
||||||
OAUTH3._browser.closeFrame(authReq.state, { debug: opts.debug || tokens.debug });
|
|
||||||
|
|
||||||
return tokens;
|
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) {
|
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);
|
resolve(params);
|
||||||
cleanup();
|
cleanup();
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user