renamed
This commit is contained in:
parent
db284fbf91
commit
dedd851ff9
|
@ -1,3 +1,5 @@
|
|||
prefactor
|
||||
.well-known
|
||||
node_modules/
|
||||
DS_Store
|
||||
.vscode
|
||||
.vscode
|
||||
|
|
10
README.md
10
README.md
|
@ -19,13 +19,13 @@ If you have no idea what you're doing
|
|||
|
||||
1. Create a folder for your project named after your app, such as `example.com/`
|
||||
2. Inside of the folder `example.com/` a folder called `assets/`
|
||||
3. Inside of the folder `example.com/assets` a folder called `org.oauth3/`
|
||||
3. Inside of the folder `example.com/assets` a folder called `oauth3.org/`
|
||||
4. Download [oauth3.js-v1.zip](https://git.daplie.com/OAuth3/oauth3.js/repository/archive.zip?ref=v1)
|
||||
5. Double-click to unzip the folder.
|
||||
6. Copy the file `oauth3.core.js` into the folder `example.com/assets/org.oauth3/`
|
||||
6. Copy the file `oauth3.core.js` into the folder `example.com/assets/oauth3.org/`
|
||||
7. Copy the folder `well-known` into the folder `example.com/`
|
||||
8. Rename the folder `well-known` to `.well-known` (when you do this, it become invisible, that's okay)
|
||||
9. Add `<script src="assets/org.oauth3/oauth3.core.js"></script>` to your `index.html`
|
||||
9. Add `<script src="assets/oauth3.org/oauth3.core.js"></script>` to your `index.html`
|
||||
9. Add `<script src="app.js"></script>` to your `index.html`
|
||||
10. Create files in `example.com` called `app.js` and `index.html` and put this in it:
|
||||
|
||||
|
@ -44,7 +44,7 @@ If you have no idea what you're doing
|
|||
<script src="https://code.jquery.com/jquery-3.1.1.js"
|
||||
integrity="sha256-16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA="
|
||||
crossorigin="anonymous"></script>
|
||||
<script src="assets/org.oauth3/oauth3.core.js"></script>
|
||||
<script src="assets/oauth3.org/oauth3.core.js"></script>
|
||||
<script src="app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -81,7 +81,7 @@ function onClickLogin() {
|
|||
console.info('Secure PPID (aka subject):', session.token.sub);
|
||||
|
||||
return oauth3.request({
|
||||
url: 'https://oauth3.org/api/issuer@oauth3.org/inspect_token'
|
||||
url: 'https://oauth3.org/api/issuer@oauth3.org/inspect'
|
||||
, session: session
|
||||
}).then(function (resp) {
|
||||
|
||||
|
|
|
@ -131,8 +131,8 @@ parseArgs(process.argv, {
|
|||
|
||||
// authn / authz
|
||||
, [ 'devices', 'manages devices for your account(s)' ]
|
||||
, [ 'devices:new', 'create a new device (default name is hostname, default ip is the result of :provider/api/org.oauth3.tunnel/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
|
||||
, [ 'devices:set', 'set the ip address of the device (defaults ip is the result of :provider/api/org.oauth3.tunnel/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
|
||||
, [ 'devices:new', 'create a new device (default name is hostname, default ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
|
||||
, [ 'devices:set', 'set the ip address of the device (defaults ip is the result of :provider/api/tunnel@oauth3.org/checkip)'.replace(/\b:provider\b/, defaults.provider) ]
|
||||
, [ 'devices:attach', "attach a device to a domain's DNS record" ]
|
||||
, [ 'devices:detach', "detach an account from a domain's DNS record" ]
|
||||
, [ 'devices:select', '(re)claim the specified device as this device (i.e. you re-installed your OS or deleted your ~/.oauth3)' ]
|
||||
|
|
342
oauth3.core.js
342
oauth3.core.js
|
@ -78,7 +78,7 @@
|
|||
, uri: {
|
||||
normalize: function (uri) {
|
||||
if ('string' !== typeof uri) {
|
||||
console.error((new Error('stack')).stack);
|
||||
throw new Error("attempted to normalize non-string URI: "+JSON.stringify(uri));
|
||||
}
|
||||
// tested with
|
||||
// example.com
|
||||
|
@ -94,7 +94,7 @@
|
|||
, url: {
|
||||
normalize: function (url) {
|
||||
if ('string' !== typeof url) {
|
||||
console.error((new Error('stack')).stack);
|
||||
throw new Error("attempted to normalize non-string URL: "+JSON.stringify(url));
|
||||
}
|
||||
// tested with
|
||||
// example.com
|
||||
|
@ -168,9 +168,12 @@
|
|||
}
|
||||
}
|
||||
, scope: {
|
||||
stringify: function (scope) {
|
||||
parse: function (scope) {
|
||||
return (scope||'').split(/[+, ]+/g);
|
||||
}
|
||||
, stringify: function (scope) {
|
||||
if (Array.isArray(scope)) {
|
||||
scope = scope.join(' ');
|
||||
scope = scope.join(',');
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
@ -204,40 +207,90 @@
|
|||
}
|
||||
, jwt: {
|
||||
// decode only (no verification)
|
||||
decode: function (str) {
|
||||
decode: function (token, opts) {
|
||||
|
||||
// 'abc.qrs.xyz'
|
||||
// [ 'abc', 'qrs', 'xyz' ]
|
||||
// [ {}, {}, 'foo' ]
|
||||
// { header: {}, payload: {}, signature: '' }
|
||||
var parts = str.split(/\./g);
|
||||
var jsons = parts.slice(0, 2).map(function (urlsafe64) {
|
||||
return JSON.parse(OAUTH3._base64.decodeUrlSafe(urlsafe64));
|
||||
});
|
||||
// {}
|
||||
var parts = token.split(/\./g);
|
||||
var err;
|
||||
if (parts.length !== 3) {
|
||||
err = new Error("Invalid JWT: required 3 '.' separated components not "+parts.length);
|
||||
err.code = 'E_INVALID_JWT';
|
||||
throw err;
|
||||
}
|
||||
|
||||
return { header: jsons[0], payload: jsons[1] };
|
||||
if (!opts || !opts.complete) {
|
||||
return JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]));
|
||||
}
|
||||
return {
|
||||
header: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[0]))
|
||||
, payload: JSON.parse(OAUTH3._base64.decodeUrlSafe(parts[1]))
|
||||
};
|
||||
}
|
||||
, verify: function (jwk, token) {
|
||||
, verify: function (token, jwk) {
|
||||
if (!OAUTH3.crypto) {
|
||||
return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
|
||||
}
|
||||
jwk = jwk.publicKey || jwk;
|
||||
|
||||
var parts = token.split(/\./g);
|
||||
var data = OAUTH3._binStr.binStrToBuffer(parts.slice(0, 2).join('.'));
|
||||
var signature = OAUTH3._base64.urlSafeToBuffer(parts[2]);
|
||||
|
||||
return OAUTH3.crypto.core.verify(jwk, data, signature);
|
||||
return OAUTH3.crypto.core.verify(jwk, data, signature).then(function () {
|
||||
return OAUTH3.jwt.decode(token);
|
||||
});
|
||||
}
|
||||
, freshness: function (tokenMeta, staletime, _now) {
|
||||
staletime = staletime || (15 * 60);
|
||||
var now = _now || Date.now();
|
||||
var fresh = ((parseInt(tokenMeta.exp, 10) || 0) - Math.round(now / 1000));
|
||||
, sign: function (payload, jwk) {
|
||||
if (!OAUTH3.crypto) {
|
||||
return OAUTH3.PromiseA.reject(new Error("OAuth3 crypto library unavailable"));
|
||||
}
|
||||
jwk = jwk.private_key || jwk.privateKey || jwk;
|
||||
|
||||
if (fresh >= staletime) {
|
||||
var prom;
|
||||
if (jwk.kid) {
|
||||
prom = OAUTH3.PromiseA.resolve(jwk.kid);
|
||||
} else {
|
||||
prom = OAUTH3.crypto.thumbprintJwk(jwk);
|
||||
}
|
||||
|
||||
return prom.then(function (kid) {
|
||||
// Currently the crypto part of the OAuth3 library only supports ES256
|
||||
var header = {type: 'JWT', alg: 'ES256', kid: kid};
|
||||
var input = [
|
||||
OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
|
||||
, OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
|
||||
].join('.');
|
||||
|
||||
return OAUTH3.crypto.core.sign(jwk, OAUTH3._binStr.binStrToBuffer(input))
|
||||
.then(OAUTH3._base64.bufferToUrlSafe)
|
||||
.then(function (signature) {
|
||||
return input + '.' + signature;
|
||||
});
|
||||
});
|
||||
}
|
||||
, freshness: function (tokenMeta, staletime, now) {
|
||||
// If the token doesn't expire then it's always fresh.
|
||||
if (!tokenMeta.exp) {
|
||||
return 'fresh';
|
||||
}
|
||||
|
||||
if (fresh <= 0) {
|
||||
return 'expired';
|
||||
staletime = staletime || (15 * 60);
|
||||
now = now || Date.now();
|
||||
// This particular number used to check if time is in milliseconds or seconds will work
|
||||
// for any date between the years 1973 and 5138.
|
||||
if (now > 1e11) {
|
||||
now = Math.round(now / 1000);
|
||||
}
|
||||
var exp = parseInt(tokenMeta.exp, 10) || 0;
|
||||
if (exp < now) {
|
||||
return 'expired';
|
||||
} else if (exp < now + staletime) {
|
||||
return 'stale';
|
||||
} else {
|
||||
return 'fresh';
|
||||
}
|
||||
|
||||
return 'stale';
|
||||
}
|
||||
}
|
||||
, urls: {
|
||||
|
@ -275,7 +328,7 @@
|
|||
// 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
|
||||
// GET https://example.com/api/issuer@oauth3.org/authorization_dialog
|
||||
// ?response_type=token
|
||||
// &scope=`encodeURIComponent('profile.login profile.email')`
|
||||
// &state=`cryptoutil.random().toString('hex')`
|
||||
|
@ -341,29 +394,36 @@
|
|||
// , "username": "<<username>>", "password": "password" }
|
||||
//
|
||||
opts = opts || {};
|
||||
var type = 'access_token';
|
||||
var grantType = 'refresh_token';
|
||||
var refresh_token = opts.refresh_token || (opts.session && opts.session.refresh_token);
|
||||
var err;
|
||||
if (!refresh_token) {
|
||||
err = new Error('refreshing a token requires a refresh token');
|
||||
err.code = 'E_NO_TOKEN';
|
||||
throw err;
|
||||
}
|
||||
if (OAUTH3.jwt.freshness(OAUTH3.jwt.decode(refresh_token)) === 'expired') {
|
||||
err = new Error('refresh token has also expired, login required again');
|
||||
err.code = 'E_EXPIRED_TOKEN';
|
||||
throw err;
|
||||
}
|
||||
|
||||
var scope = opts.scope || directive.authn_scope;
|
||||
var clientSecret = opts.client_secret;
|
||||
var args = directive[type];
|
||||
var args = directive.access_token;
|
||||
var params = {
|
||||
"grant_type": grantType
|
||||
, "refresh_token": opts.refresh_token || (opts.session && opts.session.refresh_token)
|
||||
"grant_type": 'refresh_token'
|
||||
, "refresh_token": 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) {
|
||||
if (opts.client_secret) {
|
||||
// TODO not allowed in the browser
|
||||
console.warn("if this is a browser, you must not use client_secret");
|
||||
params.client_secret = clientSecret;
|
||||
params.client_secret = opts.client_secret;
|
||||
}
|
||||
|
||||
if (scope) {
|
||||
|
@ -430,42 +490,44 @@
|
|||
}
|
||||
}
|
||||
, hooks: {
|
||||
directives: {
|
||||
get: function (providerUri) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._getCached(providerUri)
|
||||
|| OAUTH3.hooks.directives._get(providerUri))
|
||||
.then(function (directives) {
|
||||
// or do .then(this._set) to keep DRY?
|
||||
OAUTH3.hooks.directives._cache[providerUri] = directives;
|
||||
return directives;
|
||||
});
|
||||
_checkStorage: function (grpName, funcName) {
|
||||
if (!OAUTH3._hooks) {
|
||||
OAUTH3._hooks = {};
|
||||
}
|
||||
, _getCached: function (providerUri) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
|
||||
return OAUTH3.hooks.directives._cache[providerUri];
|
||||
if (!OAUTH3._hooks[grpName]) {
|
||||
console.log('using default storage for '+grpName+', set OAUTH3._hooks.'+grpName+' for custom storage');
|
||||
OAUTH3._hooks[grpName] = OAUTH3._defaultStorage[grpName];
|
||||
}
|
||||
if (!OAUTH3._hooks[grpName][funcName]) {
|
||||
throw new Error("'"+funcName+"' is not defined for custom "+grpName+" storage");
|
||||
}
|
||||
}
|
||||
, directives: {
|
||||
get: function (providerUri) {
|
||||
OAUTH3.hooks._checkStorage('directives', 'get');
|
||||
|
||||
if (!providerUri) {
|
||||
throw new Error("providerUri is not set");
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.get(OAUTH3.uri.normalize(providerUri)));
|
||||
}
|
||||
, set: function (providerUri, directives) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
if (!OAUTH3.hooks.directives._cache) { OAUTH3.hooks.directives._cache = {}; }
|
||||
OAUTH3.hooks.directives._cache[providerUri] = directives;
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.directives._set(providerUri, directives));
|
||||
}
|
||||
, _get: function (providerUri) {
|
||||
if (!OAUTH3._hooks || !OAUTH3._hooks.directives || !OAUTH3._hooks.directives.get) {
|
||||
console.warn('[Warn] Please implement OAUTH3._hooks.directives.get = function (providerUri) { return PromiseA<directives>; }');
|
||||
return JSON.parse(window.localStorage.getItem('directives-' + providerUri) || '{}');
|
||||
OAUTH3.hooks._checkStorage('directives', 'set');
|
||||
|
||||
if (!providerUri) {
|
||||
throw new Error("providerUri is not set");
|
||||
}
|
||||
return OAUTH3._hooks.directives.get(providerUri);
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.set(OAUTH3.uri.normalize(providerUri), directives));
|
||||
}
|
||||
, _set: function (providerUri, directives) {
|
||||
if (!OAUTH3._hooks || !OAUTH3._hooks.directives || !OAUTH3._hooks.directives.set) {
|
||||
console.warn('[Warn] Please implement OAUTH3._hooks.directives.set = function (providerUri, directives) { return PromiseA<directives>; }');
|
||||
window.localStorage.setItem('directives-' + providerUri, JSON.stringify(directives));
|
||||
return directives;
|
||||
}
|
||||
return OAUTH3._hooks.directives.set(providerUri, directives);
|
||||
, all: function () {
|
||||
OAUTH3.hooks._checkStorage('directives', 'all');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.all());
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3.hooks._checkStorage('directives', 'clear');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.directives.clear());
|
||||
}
|
||||
}
|
||||
, session: {
|
||||
|
@ -485,7 +547,7 @@
|
|||
oldSession.client_uri = clientUri; // azp
|
||||
|
||||
// info about the newly-discovered token
|
||||
oldSession.token = OAUTH3.jwt.decode(oldSession.access_token).payload;
|
||||
oldSession.token = OAUTH3.jwt.decode(oldSession.access_token);
|
||||
|
||||
oldSession.token.sub = oldSession.token.sub
|
||||
|| (oldSession.token.acx||{}).id
|
||||
|
@ -496,7 +558,7 @@
|
|||
oldSession.token.provider_uri = providerUri;
|
||||
|
||||
if (oldSession.refresh_token) {
|
||||
oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token).payload;
|
||||
oldSession.refresh = OAUTH3.jwt.decode(oldSession.refresh_token);
|
||||
oldSession.refresh.sub = oldSession.refresh.sub
|
||||
|| (oldSession.refresh.acx||{}).id
|
||||
|| ((oldSession.refresh.axs||[])[0]||{}).appScopedId
|
||||
|
@ -506,7 +568,7 @@
|
|||
}
|
||||
|
||||
// set for a set of audiences
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session.set(providerUri, oldSession));
|
||||
return OAUTH3.hooks.session.set(providerUri, oldSession);
|
||||
}
|
||||
, check: function (preq, opts) {
|
||||
opts = opts || {};
|
||||
|
@ -559,62 +621,40 @@
|
|||
return newSession; // oauth3.hooks.refreshSession(expiredSession, newSession);
|
||||
});
|
||||
}
|
||||
, _getCached: function (providerUri, id) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
if (!OAUTH3.hooks.session._cache) { OAUTH3.hooks.session._cache = {}; }
|
||||
if (id) {
|
||||
return OAUTH3.hooks.session._cache[providerUri + id];
|
||||
}
|
||||
return OAUTH3.hooks.session._cache[providerUri];
|
||||
}
|
||||
, set: function (providerUri, newSession, id) {
|
||||
OAUTH3.hooks._checkStorage('sessions', 'set');
|
||||
|
||||
if (!providerUri) {
|
||||
console.error(new Error('no providerUri').stack);
|
||||
throw new Error("providerUri is not set");
|
||||
}
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
if (!OAUTH3.hooks.session._cache) { OAUTH3.hooks.session._cache = {}; }
|
||||
OAUTH3.hooks.session._cache[providerUri + (id || newSession.id || newSession.token.id || '')] = newSession;
|
||||
if (!id) {
|
||||
OAUTH3.hooks.session._cache[providerUri] = newSession;
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3.hooks.session._set(providerUri, newSession));
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.set(providerUri, newSession, id));
|
||||
}
|
||||
, get: function (providerUri, id) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
OAUTH3.hooks._checkStorage('sessions', 'get');
|
||||
|
||||
if (!providerUri) {
|
||||
throw new Error("providerUri is not set");
|
||||
}
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.get(providerUri, id));
|
||||
}
|
||||
, all: function (providerUri) {
|
||||
OAUTH3.hooks._checkStorage('sessions', 'all');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(
|
||||
OAUTH3.hooks.session._getCached(providerUri, id) || OAUTH3.hooks.session._get(providerUri, id)
|
||||
).then(function (session) {
|
||||
var s = session || { token: {} };
|
||||
OAUTH3.hooks.session._cache[providerUri + (id || s.id || s.token.id || '')] = session;
|
||||
if (!id) {
|
||||
OAUTH3.hooks.session._cache[providerUri] = session;
|
||||
}
|
||||
return session;
|
||||
});
|
||||
if (providerUri) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.all(providerUri));
|
||||
}
|
||||
, _get: function (providerUri, id) {
|
||||
if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.all) {
|
||||
console.warn('[Warn] Please implement OAUTH3._hooks.sessions.all = function ([providerUri]) { return PromiseA<sessions>; }');
|
||||
, clear: function (providerUri) {
|
||||
OAUTH3.hooks._checkStorage('sessions', 'clear');
|
||||
|
||||
if (providerUri) {
|
||||
providerUri = OAUTH3.uri.normalize(providerUri);
|
||||
}
|
||||
if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.get) {
|
||||
console.warn('[Warn] Please implement OAUTH3._hooks.sessions.get = function (providerUri[, id]) { return PromiseA<session>; }');
|
||||
return JSON.parse(window.sessionStorage.getItem('session-' + providerUri + (id || '')) || 'null');
|
||||
}
|
||||
return OAUTH3._hooks.sessions.get(providerUri, id);
|
||||
}
|
||||
, _set: function (providerUri, newSession, id) {
|
||||
if (!OAUTH3._hooks || !OAUTH3._hooks.sessions || !OAUTH3._hooks.sessions.set) {
|
||||
console.warn('[Warn] Please implement OAUTH3._hooks.sessions.set = function (providerUri, newSession[, id]) { return PromiseA<newSession>; }');
|
||||
window.sessionStorage.setItem('session-' + providerUri, JSON.stringify(newSession));
|
||||
window.sessionStorage.setItem('session-' + providerUri + (id || newSession.id || newSession.token.id || ''), JSON.stringify(newSession));
|
||||
return newSession;
|
||||
}
|
||||
return OAUTH3._hooks.sessions.set(providerUri, newSession, id);
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.clear(providerUri));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -803,7 +843,7 @@
|
|||
return OAUTH3.PromiseA.reject(OAUTH3.error.parse(directives.issuer /*providerUri*/, params));
|
||||
}
|
||||
|
||||
OAUTH3.hooks.session._cache = {};
|
||||
OAUTH3.hooks.session.clear();
|
||||
return params;
|
||||
});
|
||||
}
|
||||
|
@ -1123,6 +1163,82 @@
|
|||
};
|
||||
OAUTH3.login = OAUTH3.implicitGrant;
|
||||
|
||||
OAUTH3._defaultStorage = {
|
||||
_getStorageKeys: function (prefix, storage) {
|
||||
storage = storage || window.localStorage;
|
||||
var matching = [];
|
||||
var ind, key;
|
||||
for (ind = 0; ind < storage.length; ind++) {
|
||||
key = storage.key(ind);
|
||||
if (key.indexOf(prefix || '') === 0) {
|
||||
matching.push(key);
|
||||
}
|
||||
}
|
||||
return matching;
|
||||
}
|
||||
, directives: {
|
||||
prefix: 'directives-'
|
||||
, get: function (providerUri) {
|
||||
var result = JSON.parse(window.localStorage.getItem(this.prefix + providerUri) || '{}');
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, set: function (providerUri, directives) {
|
||||
window.localStorage.setItem(this.prefix + providerUri, JSON.stringify(directives));
|
||||
return this.get(providerUri);
|
||||
}
|
||||
, all: function () {
|
||||
var prefix = this.prefix;
|
||||
var result = {};
|
||||
OAUTH3._defaultStorage._getStorageKeys(prefix).forEach(function (key) {
|
||||
result[key.replace(prefix, '')] = JSON.parse(window.localStorage.getItem(key) || '{}');
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3._defaultStorage._getStorageKeys(this.prefix).forEach(function (key) {
|
||||
window.localStorage.removeItem(key);
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve();
|
||||
}
|
||||
}
|
||||
, sessions: {
|
||||
prefix: 'session-'
|
||||
, get: function (providerUri, id) {
|
||||
var result;
|
||||
if (id) {
|
||||
result = JSON.parse(window.sessionStorage.getItem(this.prefix + providerUri+id) || 'null');
|
||||
} else {
|
||||
result = JSON.parse(window.sessionStorage.getItem(this.prefix + providerUri) || 'null');
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, set: function (providerUri, newSession, id) {
|
||||
var str = JSON.stringify(newSession);
|
||||
window.sessionStorage.setItem(this.prefix + providerUri, str);
|
||||
id = id || newSession.id || newSession.token.sub || newSession.token.id;
|
||||
if (id) {
|
||||
window.sessionStorage.setItem(this.prefix + providerUri + id, str);
|
||||
}
|
||||
return this.get(providerUri, id);
|
||||
}
|
||||
, all: function (providerUri) {
|
||||
var prefix = this.prefix + (providerUri || '');
|
||||
var result = {};
|
||||
OAUTH3._defaultStorage._getStorageKeys(prefix, window.sessionStorage).forEach(function (key) {
|
||||
result[key.replace(prefix, '')] = JSON.parse(window.sessionStorage.getItem(key) || 'null');
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, clear: function (providerUri) {
|
||||
var prefix = this.prefix + (providerUri || '');
|
||||
OAUTH3._defaultStorage._getStorageKeys(prefix, window.sessionStorage).forEach(function (key) {
|
||||
window.sessionStorage.removeItem(key);
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO get rid of these
|
||||
OAUTH3.utils = {
|
||||
clientUri: OAUTH3.clientUri
|
||||
|
@ -1154,7 +1270,7 @@
|
|||
, _resourceProviderUri: null
|
||||
, _identityProviderDirectives: null
|
||||
, _resourceProviderDirectives: null
|
||||
//, _resourceProviderMap: null // map between xyz.com and org.oauth3.domains
|
||||
//, _resourceProviderMap: null // map between xyz.com and domains@oauth3.org
|
||||
, _init: function (location, opts) {
|
||||
var me = this;
|
||||
if (!opts) {
|
||||
|
@ -1223,7 +1339,7 @@
|
|||
var me = this;
|
||||
return me._initClient().then(function () {
|
||||
return me.setIdentityProvider(providerUri).then(function () {
|
||||
// TODO how to say "Use xyz.com for org.oauth3.domains, but abc.com for org.oauth3.dns"?
|
||||
// TODO how to say "Use xyz.com for domains@oauth3.org, but abc.com for dns@oauth3.org"?
|
||||
return me.setResourceProvider(providerUri);
|
||||
});
|
||||
});
|
||||
|
|
100
oauth3.crypto.js
100
oauth3.crypto.js
|
@ -1,5 +1,5 @@
|
|||
;(function (exports) {
|
||||
'use strict';
|
||||
'use strict';
|
||||
|
||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
|
||||
|
||||
|
@ -82,6 +82,7 @@
|
|||
};
|
||||
|
||||
function checkWebCrypto() {
|
||||
/* global OAUTH3_crypto_fallback */
|
||||
var loadFallback = function() {
|
||||
var prom;
|
||||
loadFallback = function () { return prom; };
|
||||
|
@ -96,17 +97,16 @@
|
|||
resolve();
|
||||
}
|
||||
};
|
||||
script.src = '/assets/org.oauth3/oauth3.crypto.fallback.js';
|
||||
script.src = '/assets/oauth3.org/oauth3.crypto.fallback.js';
|
||||
body.appendChild(script);
|
||||
});
|
||||
return prom;
|
||||
};
|
||||
function checkException(name, func) {
|
||||
new OAUTH3.PromiseA(function (resolve) { resolve(func()); })
|
||||
OAUTH3.PromiseA.resolve().then(func)
|
||||
.then(function () {
|
||||
OAUTH3.crypto.core[name] = webCrypto[name];
|
||||
})
|
||||
.catch(function (err) {
|
||||
}, function (err) {
|
||||
console.warn('error with WebCrypto', name, '- using fallback', err);
|
||||
loadFallback().then(function () {
|
||||
OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name];
|
||||
|
@ -195,101 +195,61 @@
|
|||
.then(OAUTH3._base64.bufferToUrlSafe);
|
||||
};
|
||||
|
||||
OAUTH3.crypto._createKey = function (ppid) {
|
||||
var saltProm = OAUTH3.crypto.core.randomBytes(16);
|
||||
var kekProm = saltProm.then(function (salt) {
|
||||
return OAUTH3.crypto.core.pbkdf2(ppid, salt);
|
||||
});
|
||||
|
||||
var ecdsaProm = OAUTH3.crypto.core.genEcdsaKeyPair()
|
||||
.then(function (keyPair) {
|
||||
OAUTH3.crypto.createKeyPair = function () {
|
||||
// TODO: maybe support other types of key pairs, not just ECDSA P-256
|
||||
return OAUTH3.crypto.core.genEcdsaKeyPair().then(function (keyPair) {
|
||||
return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) {
|
||||
keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256';
|
||||
keyPair.privateKey.kid = keyPair.publicKey.kid = kid;
|
||||
return keyPair;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.crypto.encryptKeyPair = function (keyPair, password) {
|
||||
var saltProm = OAUTH3.crypto.core.randomBytes(16);
|
||||
var kekProm = saltProm.then(function (salt) {
|
||||
return OAUTH3.crypto.core.pbkdf2(password, salt);
|
||||
});
|
||||
|
||||
return OAUTH3.PromiseA.all([
|
||||
kekProm
|
||||
, ecdsaProm
|
||||
, saltProm
|
||||
, OAUTH3.crypto.core.randomBytes(16)
|
||||
, OAUTH3.crypto.core.randomBytes(12)
|
||||
, OAUTH3.crypto.core.randomBytes(12)
|
||||
]).then(function (results) {
|
||||
, ]).then(function (results) {
|
||||
var kek = results[0];
|
||||
var keyPair = results[1];
|
||||
var salt = results[2];
|
||||
var userSecret = results[3];
|
||||
var ecdsaIv = results[4];
|
||||
var secretIv = results[5];
|
||||
var salt = results[1];
|
||||
var ecdsaIv = results[2];
|
||||
|
||||
return OAUTH3.PromiseA.all([
|
||||
OAUTH3.crypto.core.encrypt(kek, ecdsaIv, OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey)))
|
||||
, OAUTH3.crypto.core.encrypt(kek, secretIv, userSecret)
|
||||
])
|
||||
.then(function (encrypted) {
|
||||
var privKeyBuf = OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey));
|
||||
return OAUTH3.crypto.core.encrypt(kek, ecdsaIv, privKeyBuf).then(function (encrypted) {
|
||||
return {
|
||||
publicKey: keyPair.publicKey
|
||||
, privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0])
|
||||
, userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
|
||||
, privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted)
|
||||
, salt: OAUTH3._base64.bufferToUrlSafe(salt)
|
||||
, ecdsaIv: OAUTH3._base64.bufferToUrlSafe(ecdsaIv)
|
||||
, secretIv: OAUTH3._base64.bufferToUrlSafe(secretIv)
|
||||
};
|
||||
, };
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.crypto._decryptKey = function (ppid, storedObj) {
|
||||
OAUTH3.crypto.decryptKeyPair = function (storedObj, password) {
|
||||
var salt = OAUTH3._base64.urlSafeToBuffer(storedObj.salt);
|
||||
var encJwk = OAUTH3._base64.urlSafeToBuffer(storedObj.privateKey);
|
||||
var iv = OAUTH3._base64.urlSafeToBuffer(storedObj.ecdsaIv);
|
||||
|
||||
return OAUTH3.crypto.core.pbkdf2(ppid, salt)
|
||||
return OAUTH3.crypto.core.pbkdf2(password, salt)
|
||||
.then(function (key) {
|
||||
return OAUTH3.crypto.core.decrypt(key, iv, encJwk);
|
||||
})
|
||||
.then(OAUTH3._binStr.bufferToBinStr)
|
||||
.then(JSON.parse);
|
||||
};
|
||||
|
||||
OAUTH3.crypto._getKey = function (ppid) {
|
||||
return OAUTH3.crypto.core.sha256(OAUTH3._binStr.binStrToBuffer(ppid))
|
||||
.then(function (hash) {
|
||||
var name = 'kek-' + OAUTH3._base64.bufferToUrlSafe(hash);
|
||||
var promise;
|
||||
|
||||
if (window.localStorage.getItem(name) === null) {
|
||||
promise = OAUTH3.crypto._createKey(ppid).then(function (key) {
|
||||
window.localStorage.setItem(name, JSON.stringify(key));
|
||||
return key;
|
||||
});
|
||||
} else {
|
||||
promise = OAUTH3.PromiseA.resolve(JSON.parse(window.localStorage.getItem(name)));
|
||||
}
|
||||
|
||||
return promise.then(function (storedObj) {
|
||||
return OAUTH3.crypto._decryptKey(ppid, storedObj);
|
||||
.then(JSON.parse)
|
||||
.then(function (privateKey) {
|
||||
return {
|
||||
privateKey: privateKey
|
||||
, publicKey: storedObj.publicKey
|
||||
, };
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.crypto._signPayload = function (payload) {
|
||||
return OAUTH3.crypto._getKey('some PPID').then(function (key) {
|
||||
var header = {type: 'JWT', alg: key.alg, kid: key.kid};
|
||||
var input = [
|
||||
OAUTH3._base64.encodeUrlSafe(JSON.stringify(header, null))
|
||||
, OAUTH3._base64.encodeUrlSafe(JSON.stringify(payload, null))
|
||||
].join('.');
|
||||
|
||||
return OAUTH3.crypto.core.sign(key, OAUTH3._binStr.binStrToBuffer(input))
|
||||
.then(OAUTH3._base64.bufferToUrlSafe)
|
||||
.then(function (signature) {
|
||||
return input + '.' + signature;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}('undefined' !== typeof exports ? exports : window));
|
||||
|
|
711
oauth3.issuer.js
711
oauth3.issuer.js
|
@ -3,39 +3,6 @@
|
|||
|
||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
|
||||
|
||||
OAUTH3.query.parse = function (search) {
|
||||
// parse a query or a hash
|
||||
if (-1 !== ['#', '?'].indexOf(search[0])) {
|
||||
search = search.substring(1);
|
||||
}
|
||||
// Solve for case of search within hash
|
||||
// example: #/authorization_dialog/?state=...&redirect_uri=...
|
||||
var queryIndex = search.indexOf('?');
|
||||
if (-1 !== queryIndex) {
|
||||
search = search.substr(queryIndex + 1);
|
||||
}
|
||||
|
||||
var args = search.split('&');
|
||||
var argsParsed = {};
|
||||
var i, arg, kvp, key, value;
|
||||
|
||||
for (i = 0; i < args.length; i += 1) {
|
||||
arg = args[i];
|
||||
if (-1 === arg.indexOf('=')) {
|
||||
argsParsed[decodeURIComponent(arg).trim()] = true;
|
||||
}
|
||||
else {
|
||||
kvp = arg.split('=');
|
||||
key = decodeURIComponent(kvp[0]).trim();
|
||||
value = decodeURIComponent(kvp[1]).trim();
|
||||
argsParsed[key] = value;
|
||||
}
|
||||
}
|
||||
return argsParsed;
|
||||
};
|
||||
OAUTH3.scope.parse = function (scope) {
|
||||
return (scope||'').split(/[, ]/g);
|
||||
};
|
||||
OAUTH3.url.parse = function (url) {
|
||||
// TODO browser
|
||||
// Node should replace this
|
||||
|
@ -58,8 +25,16 @@ OAUTH3.url._isRedirectHostSafe = function (referrerUrl, redirectUrl) {
|
|||
};
|
||||
OAUTH3.url.checkRedirect = function (client, query) {
|
||||
console.warn("[security] URL path checking not yet implemented");
|
||||
if (!query) {
|
||||
query = client;
|
||||
client = query.client_uri;
|
||||
}
|
||||
client = client.url || client;
|
||||
|
||||
var clientUrl = OAUTH3.url.normalize(client.url);
|
||||
// it doesn't matter who the referrer is as long as the destination
|
||||
// is an authorized destination for the client in question
|
||||
// (though it may not hurt to pass the referrer's info on to the client)
|
||||
var clientUrl = OAUTH3.url.normalize(client);
|
||||
var redirectUrl = OAUTH3.url.normalize(query.redirect_uri);
|
||||
|
||||
// General rule:
|
||||
|
@ -72,6 +47,18 @@ OAUTH3.url.checkRedirect = function (client, query) {
|
|||
return true;
|
||||
}
|
||||
|
||||
var callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK?'+OAUTH3.query.stringify({
|
||||
'redirect_uri': redirectUrl
|
||||
, 'allowed_urls': clientUrl
|
||||
, 'client_id': client
|
||||
, 'referrer_uri': OAUTH3.uri.normalize(window.document.referrer)
|
||||
});
|
||||
if (query.debug) {
|
||||
console.log('Redirect Attack');
|
||||
console.log(query);
|
||||
window.alert("DEBUG MODE checkRedirect error encountered. Check the console.");
|
||||
}
|
||||
location.href = callbackUrl;
|
||||
return false;
|
||||
};
|
||||
OAUTH3.url.redirect = function (clientParams, grants, tokenOrError) {
|
||||
|
@ -110,13 +97,11 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
|
|||
// Example Resource Owner Password Request
|
||||
// (generally for 1st party and direct-partner mobile apps, and webapps)
|
||||
//
|
||||
// POST https://example.com/api/org.oauth3.provider/access_token
|
||||
// POST https://example.com/api/issuer@oauth3.org/access_token
|
||||
// { "grant_type": "password", "client_id": "<<id>>", "scope": "<<scope>>"
|
||||
// , "username": "<<username>>", "password": "password" }
|
||||
//
|
||||
opts = opts || {};
|
||||
var type = 'access_token';
|
||||
var grantType = 'password';
|
||||
|
||||
if (!opts.password) {
|
||||
if (opts.otp) {
|
||||
|
@ -125,16 +110,13 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
|
|||
}
|
||||
}
|
||||
|
||||
var scope = opts.scope || directive.authn_scope;
|
||||
var clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos;
|
||||
var clientUri = opts.client_uri;
|
||||
var args = directive[type];
|
||||
var args = directive.access_token;
|
||||
var otpCode = opts.otp || opts.otpCode || opts.otp_code || opts.otpToken || opts.otp_token || undefined;
|
||||
// TODO require user agent
|
||||
var params = {
|
||||
client_id: opts.client_id || opts.client_uri
|
||||
, client_uri: opts.client_uri
|
||||
, grant_type: grantType
|
||||
, grant_type: 'password'
|
||||
, username: opts.username
|
||||
, password: opts.password || otpCode || undefined
|
||||
, totp: opts.totp || opts.totpToken || opts.totp_token || undefined
|
||||
|
@ -149,23 +131,21 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
|
|||
//, "jwt": opts.jwt // TODO sign a proof with a previously loaded public_key
|
||||
, debug: opts.debug || undefined
|
||||
};
|
||||
var uri = args.url;
|
||||
var body;
|
||||
if (opts.totp) {
|
||||
params.totp = opts.totp;
|
||||
}
|
||||
|
||||
if (clientUri) {
|
||||
params.clientAgreeTos = clientAgreeTos;
|
||||
if (!clientAgreeTos) {
|
||||
if (opts.client_uri) {
|
||||
params.clientAgreeTos = 'oauth3.org/tos/draft'; // opts.clientAgreeTos || opts.client_agree_tos;
|
||||
if (!params.clientAgreeTos) {
|
||||
throw new Error('Developer Error: missing clientAgreeTos uri');
|
||||
}
|
||||
}
|
||||
|
||||
var scope = opts.scope || directive.authn_scope;
|
||||
if (scope) {
|
||||
params.scope = OAUTH3.scope.stringify(scope);
|
||||
}
|
||||
|
||||
var uri = args.url;
|
||||
var body;
|
||||
if ('GET' === args.method.toUpperCase()) {
|
||||
uri += '?' + OAUTH3.query.stringify(params);
|
||||
} else {
|
||||
|
@ -181,6 +161,10 @@ OAUTH3.urls.resourceOwnerPassword = function (directive, opts) {
|
|||
OAUTH3.urls.grants = function (directive, opts) {
|
||||
// directive = { issuer, authorization_decision }
|
||||
// opts = { response_type, scopes{ granted, requested, pending, accepted } }
|
||||
var grantsDir = directive.grants;
|
||||
if (!grantsDir) {
|
||||
throw new Error("provider doesn't support grants");
|
||||
}
|
||||
|
||||
if (!opts) {
|
||||
throw new Error("You must supply a directive and an options object.");
|
||||
|
@ -195,18 +179,19 @@ OAUTH3.urls.grants = function (directive, opts) {
|
|||
console.warn("You should supply options.referrer");
|
||||
}
|
||||
if (!opts.method) {
|
||||
console.warn("You must supply options.method as either 'GET', or 'POST'");
|
||||
console.warn("You should supply options.method as either 'GET', or 'POST'");
|
||||
opts.method = grantsDir.method || 'GET';
|
||||
}
|
||||
if ('POST' === opts.method) {
|
||||
if ('string' !== typeof opts.scope) {
|
||||
console.warn("You should supply options.scope as a space-delimited string of scopes");
|
||||
throw new Error("You must supply options.scope as a comma-delimited string of scopes");
|
||||
}
|
||||
if (-1 === ['token', 'code'].indexOf(opts.response_type)) {
|
||||
throw new Error("You must supply options.response_type as 'token' or 'code'");
|
||||
if ('string' !== typeof opts.sub) {
|
||||
console.log("provide 'sub' to urls.grants to specify the PPID for the client");
|
||||
}
|
||||
}
|
||||
|
||||
var url = OAUTH3.url.resolve(directive.api, directive.grants.url)
|
||||
var url = OAUTH3.url.resolve(directive.api, grantsDir.url)
|
||||
.replace(/(:azp|:client_id)/g, OAUTH3.uri.normalize(opts.client_id || opts.client_uri))
|
||||
.replace(/(:sub|:account_id)/g, opts.session.token.sub || 'ISSUER:GRANT:TOKEN_SUB:UNDEFINED')
|
||||
;
|
||||
|
@ -214,16 +199,14 @@ OAUTH3.urls.grants = function (directive, opts) {
|
|||
client_id: opts.client_id
|
||||
, client_uri: opts.client_uri
|
||||
, referrer: opts.referrer
|
||||
, response_type: opts.response_type
|
||||
, scope: opts.scope
|
||||
, tenant_id: opts.tenant_id
|
||||
, sub: opts.sub
|
||||
};
|
||||
var body;
|
||||
|
||||
var body;
|
||||
if ('GET' === opts.method) {
|
||||
url += '?' + OAUTH3.query.stringify(data);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
body = data;
|
||||
}
|
||||
|
||||
|
@ -234,6 +217,76 @@ OAUTH3.urls.grants = function (directive, opts) {
|
|||
, session: opts.session
|
||||
};
|
||||
};
|
||||
OAUTH3.urls.clientToken = function (directive, opts) {
|
||||
var tokenDir = directive.access_token;
|
||||
if (!tokenDir) {
|
||||
throw new Error("provider doesn't support getting access tokens");
|
||||
}
|
||||
|
||||
if (!opts) {
|
||||
throw new Error("You must supply a directive and an options object.");
|
||||
}
|
||||
if (!(opts.azp || opts.client_id)) {
|
||||
throw new Error("You must supply options.client_id.");
|
||||
}
|
||||
if (!opts.session) {
|
||||
throw new Error("You must supply options.session.");
|
||||
}
|
||||
if (!opts.method) {
|
||||
opts.method = tokenDir.method || 'POST';
|
||||
}
|
||||
|
||||
var params = {
|
||||
grant_type: 'issuer_token'
|
||||
, client_id: opts.azp || opts.client_id
|
||||
, azp: opts.azp || opts.client_id
|
||||
, aud: opts.aud
|
||||
, exp: opts.exp
|
||||
, refresh_token: opts.refresh_token
|
||||
, refresh_exp: opts.refresh_exp
|
||||
};
|
||||
|
||||
var url = OAUTH3.url.resolve(directive.api, tokenDir.url);
|
||||
var body;
|
||||
if ('GET' === opts.method) {
|
||||
url += '?' + OAUTH3.query.stringify(params);
|
||||
} else {
|
||||
body = params;
|
||||
}
|
||||
|
||||
return {
|
||||
method: opts.method
|
||||
, url: url
|
||||
, data: body
|
||||
, session: opts.session
|
||||
};
|
||||
};
|
||||
OAUTH3.urls.publishKey = function (directive, opts) {
|
||||
var jwkDir = directive.publish_jwk;
|
||||
if (!jwkDir) {
|
||||
throw new Error("provider doesn't support publishing public keys");
|
||||
}
|
||||
if (!opts) {
|
||||
throw new Error("You must supply a directive and an options object.");
|
||||
}
|
||||
if (!opts.session) {
|
||||
throw new Error("You must supply 'options.session'.");
|
||||
}
|
||||
if (!(opts.public_key || opts.publicKey)) {
|
||||
throw new Error("You must supply 'options.public_key'.");
|
||||
}
|
||||
|
||||
var url = OAUTH3.url.resolve(directive.api, jwkDir.url)
|
||||
.replace(/(:sub|:account_id)/g, opts.session.token.sub)
|
||||
;
|
||||
|
||||
return {
|
||||
method: jwkDir.method || opts.method || 'POST'
|
||||
, url: url
|
||||
, data: opts.public_key || opts.publicKey
|
||||
, session: opts.session
|
||||
};
|
||||
};
|
||||
|
||||
OAUTH3.authn = {};
|
||||
OAUTH3.authn.loginMeta = function (directive, opts) {
|
||||
|
@ -267,112 +320,95 @@ OAUTH3.authn.otp = function (directive, opts) {
|
|||
OAUTH3.authn.resourceOwnerPassword = function (directive, opts) {
|
||||
var providerUri = directive.issuer;
|
||||
|
||||
//var scope = opts.scope;
|
||||
//var appId = opts.appId;
|
||||
return OAUTH3.discover(providerUri, opts).then(function (directive) {
|
||||
var prequest = OAUTH3.urls.resourceOwnerPassword(directive, opts);
|
||||
return OAUTH3.request(OAUTH3.urls.resourceOwnerPassword(directive, opts)).then(function (resp) {
|
||||
var data = resp.data;
|
||||
data.provider_uri = providerUri;
|
||||
if (data.error) {
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, data));
|
||||
}
|
||||
|
||||
// TODO return not the raw request?
|
||||
return OAUTH3.request(prequest).then(function (req) {
|
||||
var data = req.data;
|
||||
data.provider_uri = providerUri;
|
||||
if (data.error) {
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, data));
|
||||
return OAUTH3.hooks.session.refresh(
|
||||
opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri }
|
||||
, data
|
||||
);
|
||||
}).then(function (session) {
|
||||
if (!opts.rememberDevice && !opts.remember_device) {
|
||||
return session;
|
||||
}
|
||||
|
||||
return OAUTH3.PromiseA.resolve().then(function () {
|
||||
if (!OAUTH3.crypto) {
|
||||
throw new Error("OAuth3 crypto library unavailable");
|
||||
}
|
||||
|
||||
return OAUTH3.hooks.session.refresh(
|
||||
opts.session || { provider_uri: providerUri, client_uri: opts.client_uri || opts.clientUri }
|
||||
, data
|
||||
);
|
||||
return OAUTH3.crypto.createKeyPair().then(function (keyPair) {
|
||||
return OAUTH3.request(OAUTH3.urls.publishKey(directive, {
|
||||
session: session
|
||||
, publicKey: keyPair.publicKey
|
||||
})).then(function () {
|
||||
return OAUTH3.hooks.keyPairs.set(session.token.sub, keyPair);
|
||||
});
|
||||
});
|
||||
}).then(function () {
|
||||
return session;
|
||||
}, function (err) {
|
||||
console.error('failed to save keys to remember device', err);
|
||||
window.alert('Failed to remember device');
|
||||
return session;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.authz = {};
|
||||
OAUTH3.authz.scopes = function (providerUri, session, clientParams) {
|
||||
// OAuth3.requests.grants(providerUri, {}); // return list of grants
|
||||
// OAuth3.checkGrants(providerUri, {}); //
|
||||
var clientUri = OAUTH3.uri.normalize(clientParams.client_uri || OAUTH3._browser.window.document.referrer);
|
||||
var scope = clientParams.scope || '';
|
||||
var clientObj = clientParams;
|
||||
|
||||
if (!scope) {
|
||||
scope = 'oauth3_authn';
|
||||
var scope = clientParams.scope || 'oauth3_authn';
|
||||
if ('oauth3_authn' === scope) {
|
||||
// implicit ppid grant is automatic
|
||||
console.warn('[security] fix scope checking on backend so that we can do automatic grants');
|
||||
// TODO check user preference if implicit ppid grant is allowed
|
||||
//return generateToken(session, clientObj);
|
||||
}
|
||||
|
||||
return OAUTH3.authz.grants(providerUri, {
|
||||
method: 'GET'
|
||||
, client_id: clientUri
|
||||
, client_uri: clientUri
|
||||
, session: session
|
||||
}).then(function (grantResults) {
|
||||
var grants;
|
||||
var grantedScopes;
|
||||
var grantedScopesMap;
|
||||
var pendingScopes;
|
||||
var acceptedScopes;
|
||||
var scopes = scope.split(/[+, ]/g);
|
||||
var callbackUrl;
|
||||
|
||||
// it doesn't matter who the referrer is as long as the destination
|
||||
// is an authorized destination for the client in question
|
||||
// (though it may not hurt to pass the referrer's info on to the client)
|
||||
if (!OAUTH3.url.checkRedirect(grantResults.client, clientObj)) {
|
||||
callbackUrl = 'https://oauth3.org/docs/errors#E_REDIRECT_ATTACK'
|
||||
+ '?redirect_uri=' + clientObj.redirect_uri
|
||||
+ '&allowed_urls=' + grantResults.client.url
|
||||
+ '&client_id=' + clientUri
|
||||
+ '&referrer_uri=' + OAUTH3.uri.normalize(window.document.referrer)
|
||||
;
|
||||
if (clientParams.debug) {
|
||||
console.log('grantResults Redirect Attack');
|
||||
console.log(grantResults);
|
||||
console.log(clientObj);
|
||||
window.alert("DEBUG MODE checkRedirect error encountered. Check the console.");
|
||||
return OAUTH3.hooks.grants.get(session.token.sub, clientUri).then(function (granted) {
|
||||
if (granted) {
|
||||
if (typeof granted.scope === 'string') {
|
||||
return OAUTH3.scope.parse(granted.scope);
|
||||
} else if (Array.isArray(granted.scope)) {
|
||||
return granted.scope;
|
||||
}
|
||||
location.href = callbackUrl;
|
||||
return;
|
||||
}
|
||||
|
||||
if ('oauth3_authn' === scope) {
|
||||
// implicit ppid grant is automatic
|
||||
console.warn('[security] fix scope checking on backend so that we can do automatic grants');
|
||||
// TODO check user preference if implicit ppid grant is allowed
|
||||
//return generateToken(session, clientObj);
|
||||
}
|
||||
|
||||
grants = (grantResults).grants.filter(function (grant) {
|
||||
if (clientUri === (grant.azp || grant.oauth_client_id || grant.oauthClientId)) {
|
||||
return true;
|
||||
return OAUTH3.authz.grants(providerUri, {
|
||||
method: 'GET'
|
||||
, client_id: clientUri
|
||||
, client_uri: clientUri
|
||||
, session: session
|
||||
}).then(function (results) {
|
||||
return results.grants;
|
||||
}, function (err) {
|
||||
if (!/no .*grants .*found/i.test(err.message)) {
|
||||
throw err;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}).then(function (granted) {
|
||||
var requested = OAUTH3.scope.parse(scope);
|
||||
var accepted = [];
|
||||
var pending = [];
|
||||
requested.forEach(function (scp) {
|
||||
if (granted.indexOf(scp) < 0) {
|
||||
pending.push(scp);
|
||||
} else {
|
||||
accepted.push(scp);
|
||||
}
|
||||
});
|
||||
|
||||
grantedScopesMap = {};
|
||||
acceptedScopes = [];
|
||||
pendingScopes = scopes.filter(function (requestedScope) {
|
||||
return grants.every(function (grant) {
|
||||
if (!grant.scope) {
|
||||
grant.scope = 'oauth3_authn';
|
||||
}
|
||||
var gscopes = grant.scope.split(/[+, ]/g);
|
||||
gscopes.forEach(function (s) { grantedScopesMap[s] = true; });
|
||||
if (-1 !== gscopes.indexOf(requestedScope)) {
|
||||
// already accepted in the past
|
||||
acceptedScopes.push(requestedScope);
|
||||
}
|
||||
else {
|
||||
// true, is pending
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
grantedScopes = Object.keys(grantedScopesMap);
|
||||
|
||||
return {
|
||||
pending: pendingScopes // not yet accepted
|
||||
, granted: grantedScopes // all granted, ever
|
||||
, requested: scopes // all requested, now
|
||||
, accepted: acceptedScopes // granted (ever) and requested (now)
|
||||
requested: requested // all requested, now
|
||||
, granted: granted // all granted, ever
|
||||
, accepted: accepted // intersection of granted (ever) and requested (now)
|
||||
, pending: pending // not yet accepted
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -381,73 +417,153 @@ OAUTH3.authz.grants = function (providerUri, opts) {
|
|||
client_id: providerUri
|
||||
, debug: opts.debug
|
||||
}).then(function (directive) {
|
||||
return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts);
|
||||
}).then(function (grantsResult) {
|
||||
var grants = grantsResult.originalData || grantsResult.data;
|
||||
if (grants.error) {
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants));
|
||||
}
|
||||
// the responses for GET and POST requests are now the same, so we should alway be able to
|
||||
// use the response and save it the same way.
|
||||
if ('GET' !== opts.method && 'POST' !== opts.method) {
|
||||
return grants;
|
||||
}
|
||||
|
||||
return OAUTH3.request(OAUTH3.urls.grants(directive, opts), opts).then(function (grantsResult) {
|
||||
if ('POST' === opts.method) {
|
||||
// TODO this is clientToken
|
||||
return grantsResult.originalData || grantsResult.data;
|
||||
}
|
||||
|
||||
var grants = grantsResult.originalData || grantsResult.data;
|
||||
// TODO
|
||||
if (grants.error) {
|
||||
return OAUTH3.PromiseA.reject(OAUTH3.error.parse(providerUri, grants));
|
||||
}
|
||||
|
||||
OAUTH3.hooks.grants.set(opts.client_id + '-client', grants.client);
|
||||
grants.grants.forEach(function (grant) {
|
||||
var clientId = grant.client_id || grant.oauth_client_id || grant.oauthClientId;
|
||||
// TODO should save as an array
|
||||
OAUTH3.hooks.grants.set(clientId, [ grant ]);
|
||||
});
|
||||
|
||||
return {
|
||||
client: OAUTH3.hooks.grants.get(opts.client_id + '-client')
|
||||
, grants: OAUTH3.hooks.grants.get(opts.client_id) || []
|
||||
};
|
||||
});
|
||||
OAUTH3.hooks.grants.set(grants.sub, grants.azp, grants);
|
||||
return {
|
||||
client: grants.azp
|
||||
, clientSub: grants.azpSub
|
||||
, grants: OAUTH3.scope.parse(grants.scope)
|
||||
};
|
||||
});
|
||||
};
|
||||
function calcExpiration(exp, now) {
|
||||
if (!exp) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof exp === 'string') {
|
||||
var match = /^(\d+\.?\d*) *(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(exp);
|
||||
if (!match) {
|
||||
return now;
|
||||
}
|
||||
var num = parseFloat(match[1]);
|
||||
var type = (match[2] || 's').toLowerCase()[0];
|
||||
switch (type) {
|
||||
case 'y': num *= 365.25; /* falls through */
|
||||
case 'd': num *= 24; /* falls through */
|
||||
case 'h': num *= 60; /* falls through */
|
||||
case 'm': num *= 60; /* falls through */
|
||||
case 's': exp = num;
|
||||
}
|
||||
}
|
||||
if (typeof exp !== 'number') {
|
||||
throw new Error('invalid expiration provided: '+exp);
|
||||
}
|
||||
|
||||
now = now || Math.floor(Date.now() / 1000);
|
||||
if (exp > now) {
|
||||
return exp;
|
||||
} else if (exp > 31557600) {
|
||||
console.warn('tried to set expiration to more that a year');
|
||||
exp = 31557600;
|
||||
}
|
||||
return now + exp;
|
||||
}
|
||||
OAUTH3.authz.redirectWithToken = function (providerUri, session, clientParams, scopes) {
|
||||
if (!OAUTH3.url.checkRedirect(clientParams.client_uri, clientParams)) {
|
||||
return;
|
||||
}
|
||||
if ('token' !== clientParams.response_type) {
|
||||
var message;
|
||||
if ('code' === clientParams.response_type) {
|
||||
message = "Authorization Code Redirect NOT IMPLEMENTED";
|
||||
} else {
|
||||
message = "Authorization response type '"+clientParams.response_type+"' not supported";
|
||||
}
|
||||
window.alert(message);
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
scopes.new = scopes.new || [];
|
||||
|
||||
if ('token' === clientParams.response_type) {
|
||||
// get token and redirect client-side
|
||||
return OAUTH3.authz.grants(providerUri, {
|
||||
method: 'POST'
|
||||
var prom;
|
||||
if (scopes.new) {
|
||||
prom = OAUTH3.authz.grants(providerUri, {
|
||||
session: session
|
||||
, method: 'POST'
|
||||
, client_id: clientParams.client_uri
|
||||
, client_uri: clientParams.client_uri
|
||||
, scope: scopes.granted.concat(scopes.new).join(',')
|
||||
, response_type: clientParams.response_type
|
||||
, referrer: clientParams.referrer
|
||||
, session: session
|
||||
, subject: clientParams.subject
|
||||
, debug: clientParams.debug
|
||||
}).then(function (results) {
|
||||
|
||||
// TODO limit refresh token to an expirable token
|
||||
// TODO inform client not to persist token
|
||||
/*
|
||||
if (clientParams.dnsTxt) {
|
||||
Object.keys(results).forEach(function (key) {
|
||||
if (/refresh/.test(key)) {
|
||||
results[key] = undefined;
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
OAUTH3.url.redirect(clientParams, scopes, results);
|
||||
, scope: scopes.accepted.concat(scopes.new).join(',')
|
||||
});
|
||||
} else {
|
||||
prom = OAUTH3.PromiseA.resolve();
|
||||
}
|
||||
else if ('code' === clientParams.response_type) {
|
||||
// get token and redirect server-side
|
||||
// (requires insecure form post as per spec)
|
||||
//OAUTH3.requests.authorizationDecision();
|
||||
window.alert("Authorization Code Redirect NOT IMPLEMENTED");
|
||||
throw new Error("Authorization Code Redirect NOT IMPLEMENTED");
|
||||
}
|
||||
|
||||
return prom.then(function () {
|
||||
return OAUTH3.hooks.keyPairs.get(session.token.sub);
|
||||
}).then(function (keyPair) {
|
||||
if (!keyPair) {
|
||||
return OAUTH3.discover(providerUri, {
|
||||
client_id: providerUri
|
||||
, debug: clientParams.debug
|
||||
}).then(function (directive) {
|
||||
return OAUTH3.request(OAUTH3.urls.clientToken(directive, {
|
||||
method: 'POST'
|
||||
, session: session
|
||||
, referrer: clientParams.referrer
|
||||
, response_type: clientParams.response_type
|
||||
, client_id: clientParams.client_uri
|
||||
, azp: clientParams.client_uri
|
||||
, aud: clientParams.aud
|
||||
, exp: clientParams.exp
|
||||
, refresh_token: clientParams.refresh_token
|
||||
, refresh_exp: clientParams.refresh_exp
|
||||
, debug: clientParams.debug
|
||||
})).then(function (result) {
|
||||
return result.originalData || result.data;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return OAUTH3.hooks.grants.get(keyPair.sub, clientParams.client_uri).then(function (grant) {
|
||||
var now = Math.floor(Date.now()/1000);
|
||||
var payload = {
|
||||
iat: now
|
||||
, iss: providerUri
|
||||
, aud: clientParams.aud || providerUri
|
||||
, azp: clientParams.client_uri
|
||||
, sub: grant.azpSub
|
||||
, scope: OAUTH3.scope.stringify(grant.scope)
|
||||
, };
|
||||
|
||||
var signProms = [];
|
||||
signProms.push(OAUTH3.jwt.sign(Object.assign({
|
||||
exp: calcExpiration(clientParams.exp || '1h', now)
|
||||
}, payload), keyPair));
|
||||
// if (clientParams.refresh_token) {
|
||||
signProms.push(OAUTH3.jwt.sign(Object.assign({
|
||||
exp: calcExpiration(clientParams.refresh_exp, now)
|
||||
}, payload), keyPair));
|
||||
// }
|
||||
return OAUTH3.PromiseA.all(signProms).then(function (tokens) {
|
||||
console.log('created new tokens for client');
|
||||
return {
|
||||
access_token: tokens[0]
|
||||
, refresh_token: tokens[1]
|
||||
, scope: OAUTH3.scope.stringify(grant.scope)
|
||||
, token_type: 'bearer'
|
||||
};
|
||||
});
|
||||
});
|
||||
}).then(function (session) {
|
||||
// TODO limit refresh token to an expirable token
|
||||
// TODO inform client not to persist token
|
||||
OAUTH3.url.redirect(clientParams, scopes, session);
|
||||
}, function (err) {
|
||||
console.error('unexpected error creating client tokens', err);
|
||||
OAUTH3.url.redirect(clientParams, scopes, {error: err});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.requests = {};
|
||||
OAUTH3.requests.accounts = {};
|
||||
OAUTH3.requests.accounts.update = function (directive, session, opts) {
|
||||
|
@ -512,25 +628,178 @@ OAUTH3.requests.accounts.create = function (directive, session, account) {
|
|||
, data: data
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3.hooks.grants = {
|
||||
// Provider Only
|
||||
set: function (clientUri, newGrants) {
|
||||
clientUri = OAUTH3.uri.normalize(clientUri);
|
||||
console.warn('[oauth3.hooks.setGrants] PLEASE IMPLEMENT -- Your Fault');
|
||||
console.warn(newGrants);
|
||||
if (!this._cache) { this._cache = {}; }
|
||||
console.log('clientUri, newGrants');
|
||||
console.log(clientUri, newGrants);
|
||||
this._cache[clientUri] = newGrants;
|
||||
return newGrants;
|
||||
get: function (id, clientUri) {
|
||||
OAUTH3.hooks._checkStorage('grants', 'get');
|
||||
|
||||
if (!id) {
|
||||
throw new Error("id is not set");
|
||||
}
|
||||
if (!clientUri) {
|
||||
throw new Error("clientUri is not set");
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.get(id, OAUTH3.uri.normalize(clientUri)));
|
||||
}
|
||||
, get: function (clientUri) {
|
||||
clientUri = OAUTH3.uri.normalize(clientUri);
|
||||
console.warn('[oauth3.hooks.getGrants] PLEASE IMPLEMENT -- Your Fault');
|
||||
if (!this._cache) { this._cache = {}; }
|
||||
console.log('clientUri, existingGrants');
|
||||
console.log(clientUri, this._cache[clientUri]);
|
||||
return this._cache[clientUri];
|
||||
, set: function (id, clientUri, grants) {
|
||||
OAUTH3.hooks._checkStorage('grants', 'set');
|
||||
|
||||
if (!id) {
|
||||
throw new Error("id is not set");
|
||||
}
|
||||
if (!clientUri) {
|
||||
throw new Error("clientUri is not set");
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.set(id, OAUTH3.uri.normalize(clientUri), grants));
|
||||
}
|
||||
, all: function () {
|
||||
OAUTH3.hooks._checkStorage('grants', 'all');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.all());
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3.hooks._checkStorage('grants', 'clear');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.grants.clear());
|
||||
}
|
||||
};
|
||||
OAUTH3.hooks.keyPairs = {
|
||||
get: function (id) {
|
||||
OAUTH3.hooks._checkStorage('keyPairs', 'get');
|
||||
|
||||
if (!id) {
|
||||
throw new Error("id is not set");
|
||||
}
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.get(id));
|
||||
}
|
||||
, set: function (id, keyPair) {
|
||||
OAUTH3.hooks._checkStorage('keyPairs', 'set');
|
||||
|
||||
if (!keyPair && id.privateKey && id.publicKey && id.sub) {
|
||||
keyPair = id;
|
||||
id = keyPair.sub;
|
||||
}
|
||||
if (!keyPair) {
|
||||
return OAUTH3.PromiseA.reject(new Error("no key pair provided to save"));
|
||||
}
|
||||
if (!id) {
|
||||
throw new Error("id is not set");
|
||||
}
|
||||
keyPair.sub = keyPair.sub || id;
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.set(id, keyPair));
|
||||
}
|
||||
, all: function () {
|
||||
OAUTH3.hooks._checkStorage('keyPairs', 'all');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.all());
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3.hooks._checkStorage('keyPairs', 'clear');
|
||||
|
||||
return OAUTH3.PromiseA.resolve(OAUTH3._hooks.keyPairs.clear());
|
||||
}
|
||||
};
|
||||
OAUTH3.hooks.session.get = function (providerUri, id) {
|
||||
OAUTH3.hooks._checkStorage('sessions', 'get');
|
||||
var sessProm = OAUTH3.PromiseA.resolve(OAUTH3._hooks.sessions.get(providerUri, id));
|
||||
if (providerUri !== OAUTH3.clientUri(window.location)) {
|
||||
return sessProm;
|
||||
}
|
||||
|
||||
return sessProm.then(function (session) {
|
||||
if (session && OAUTH3.jwt.freshness(session.token) === 'fresh') {
|
||||
return session;
|
||||
}
|
||||
|
||||
return OAUTH3.hooks.keyPairs.all().then(function (keyPairs) {
|
||||
var pair;
|
||||
if (id) {
|
||||
pair = keyPairs[id];
|
||||
} else if (Object.keys(keyPairs).length === 1) {
|
||||
id = Object.keys(keyPairs)[0];
|
||||
pair = keyPairs[id];
|
||||
} else if (Object.keys(keyPairs).length > 1) {
|
||||
console.error("too many users, don't know which key to use");
|
||||
}
|
||||
if (!pair) {
|
||||
// even if the access token isn't fresh, the session might have a refresh token
|
||||
return session;
|
||||
}
|
||||
|
||||
var now = Math.floor(Date.now()/1000);
|
||||
var payload = {
|
||||
iat: now
|
||||
, iss: providerUri
|
||||
, aud: providerUri
|
||||
, azp: providerUri
|
||||
, sub: pair.sub || id
|
||||
, scope: ''
|
||||
, exp: now + 3600
|
||||
};
|
||||
return OAUTH3.jwt.sign(payload, pair.privateKey).then(function (token) {
|
||||
console.log('created new token for provider');
|
||||
return OAUTH3.hooks.session.refresh(
|
||||
{ provider_uri: providerUri, client_uri: providerUri || providerUri }
|
||||
, { access_token: token }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
OAUTH3._defaultStorage.grants = {
|
||||
prefix: 'grants-'
|
||||
, get: function (id, clientUri) {
|
||||
var key = this.prefix + id+'/'+clientUri;
|
||||
var result = JSON.parse(window.localStorage.getItem(key) || 'null');
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, set: function (id, clientUri, grants) {
|
||||
var key = this.prefix + id+'/'+clientUri;
|
||||
window.localStorage.setItem(key, JSON.stringify(grants));
|
||||
return this.get(clientUri);
|
||||
}
|
||||
, all: function () {
|
||||
var prefix = this.prefix;
|
||||
var result = {};
|
||||
OAUTH3._defaultStorage._getStorageKeys(prefix, window.localStorage).forEach(function (key) {
|
||||
var split = key.replace(prefix, '').split('/');
|
||||
if (!result[split[0]]) { result[split[0]] = {}; }
|
||||
result[split[0]][split[1]] = JSON.parse(window.localStorage.getItem(key) || 'null');
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3._defaultStorage._getStorageKeys(this.prefix, window.localStorage).forEach(function (key) {
|
||||
window.localStorage.removeItem(key);
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve();
|
||||
}
|
||||
};
|
||||
OAUTH3._defaultStorage.keyPairs = {
|
||||
prefix: 'key_pairs-'
|
||||
, get: function (id) {
|
||||
var result = JSON.parse(window.localStorage.getItem(this.prefix + id) || 'null');
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, set: function (id, keyPair) {
|
||||
window.localStorage.setItem(this.prefix + id, JSON.stringify(keyPair));
|
||||
return this.get(id);
|
||||
}
|
||||
, all: function () {
|
||||
var prefix = this.prefix;
|
||||
var result = {};
|
||||
OAUTH3._defaultStorage._getStorageKeys(prefix, window.localStorage).forEach(function (key) {
|
||||
result[key.replace(prefix, '')] = JSON.parse(window.localStorage.getItem(key) || 'null');
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve(result);
|
||||
}
|
||||
, clear: function () {
|
||||
OAUTH3._defaultStorage._getStorageKeys(this.prefix, window.localStorage).forEach(function (key) {
|
||||
window.localStorage.removeItem(key);
|
||||
});
|
||||
return OAUTH3.PromiseA.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ OAUTH3._base64.atob = function (base64) {
|
|||
OAUTH3._base64.btoa = function (text) {
|
||||
return new Buffer(text, 'utf8').toString('base64');
|
||||
};
|
||||
OAUTH3._defaultStorage = require('./oauth3.node.storage');
|
||||
|
||||
OAUTH3._node = {};
|
||||
OAUTH3._node.discover = function(providerUri/*, opts*/) {
|
||||
|
|
|
@ -67,10 +67,9 @@ module.exports = {
|
|||
|
||||
, sessions: {
|
||||
all: function (providerUri) {
|
||||
var dirname = path.join(oauth3dir, 'sessions');
|
||||
return fs.readdirAsync(dirname).then(function (nodes) {
|
||||
return fs.readdirAsync(sessionsdir).then(function (nodes) {
|
||||
return nodes.map(function (node) {
|
||||
var result = require(path.join(dirname, node));
|
||||
var result = require(path.join(sessionsdir, node));
|
||||
if (result.link) {
|
||||
return null;
|
||||
}
|
||||
|
@ -91,7 +90,7 @@ module.exports = {
|
|||
result = require(path.join(sessionsdir, providerUri + '.json'));
|
||||
// TODO make safer
|
||||
if (result.link && '/' !== result.link[0] && !/\.\./.test(result.link)) {
|
||||
result = require(path.join(oauth3dir, 'sessions', result.link));
|
||||
result = require(path.join(sessionsdir, result.link));
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
|
@ -113,10 +112,9 @@ module.exports = {
|
|||
});
|
||||
}
|
||||
, clear: function () {
|
||||
var dirname = path.join(oauth3dir, 'sessions');
|
||||
return fs.readdirAsync(dirname).then(function (nodes) {
|
||||
return fs.readdirAsync(sessionsdir).then(function (nodes) {
|
||||
return PromiseA.all(nodes.map(function (node) {
|
||||
return fs.unlinkAsync(path.join(dirname, node));
|
||||
return fs.unlinkAsync(path.join(sessionsdir, node));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ OAUTH3.api['tunnel.token'] = function (providerUri, opts) {
|
|||
return OAUTH3.request({
|
||||
method: 'POST'
|
||||
, url: OAUTH3.url.normalize(providerUri)
|
||||
+ '/api/org.oauth3.tunnel/accounts/' + session.token.sub + '/token'
|
||||
+ '/api/tunnel@oauth3.org/accounts/' + session.token.sub + '/token'
|
||||
, session: session
|
||||
, data: {
|
||||
domains: opts.data.domains
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
{ "terms": [ "oauth3.org/tos/draft" ]
|
||||
, "api": "api.:hostname"
|
||||
, "authorization_dialog": { "url": "#/authorization_dialog" }
|
||||
, "access_token": { "method": "POST", "url": "api/issuer@oauth3.org/access_token" }
|
||||
, "otp": { "method": "POST", "url": "api/issuer@oauth3.org/otp" }
|
||||
, "credential_otp": { "method": "POST", "url": "api/issuer@oauth3.org/otp" }
|
||||
, "credential_meta": { "url": "api/issuer@oauth3.org/logins/meta/:type/:id" }
|
||||
, "credential_create": { "method": "POST", "url": "api/issuer@oauth3.org/logins" }
|
||||
, "grants": { "method": "GET", "url": "api/issuer@oauth3.org/grants/:azp/:sub" }
|
||||
, "authorization_decision": { "method": "POST", "url": "api/issuer@oauth3.org/authorization_decision" }
|
||||
, "callback": { "method": "GET", "url": ".well-known/oauth3/callback.html#/" }
|
||||
, "logout": { "method": "GET", "url": "#/logout/" }
|
||||
, "access_token": { "method": "POST", "url": "api/issuer@oauth3.org/access_token" }
|
||||
, "otp": { "method": "POST", "url": "api/issuer@oauth3.org/access_token/send_otp" }
|
||||
, "credential_otp": { "method": "POST", "url": "api/issuer@oauth3.org/access_token/send_otp" }
|
||||
, "grants": { "method": "GET", "url": "api/issuer@oauth3.org/grants/:sub/:azp" }
|
||||
, "publish_jwk": { "method": "POST", "url": "api/issuer@oauth3.org/jwks/:sub" }
|
||||
, "retrieve_jwk": { "method": "GET", "url": "api/issuer@oauth3.org/jwks/:sub/:kid.json" }
|
||||
, "callback": { "method": "GET", "url": ".well-known/oauth3/callback.html#/" }
|
||||
, "logout": { "method": "GET", "url": "#/logout/" }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue