From a4b7833989bce8162062008223d8247e51f25a81 Mon Sep 17 00:00:00 2001 From: John Shaver Date: Mon, 13 Nov 2017 13:09:58 -0800 Subject: [PATCH 1/3] OAUTH3.crypto - defer calls to asynchronously loaded methods. --- oauth3.crypto.js | 56 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/oauth3.crypto.js b/oauth3.crypto.js index d87a158..bb83280 100644 --- a/oauth3.crypto.js +++ b/oauth3.crypto.js @@ -8,6 +8,9 @@ OAUTH3.crypto.core = require('./oauth3.node.crypto'); } catch (error) { OAUTH3.crypto.core = {}; + OAUTH3.crypto.core.ready = false; + var finishBeforeReady = []; + var deferedCalls = []; // We don't currently have a fallback method for this function, so we assign // it directly to the core object instead of the webCrypto object. @@ -17,10 +20,31 @@ }; var webCrypto = {}; + + var deferCryptoCall = function(name) { + return function() { + var args = arguments; + return new OAUTH3.PromiseA(function(resolve, reject) { + deferedCalls.push(function(){ + try { + webCrypto[name].apply(webCrypto, args) + .then(function(result){ + resolve(result); + }); + } catch(e) { + reject(e); + } + }); + }); + }; + }; + + OAUTH3.crypto.core.sha256 = deferCryptoCall("sha256"); webCrypto.sha256 = function (buf) { return OAUTH3._browser.window.crypto.subtle.digest({name: 'SHA-256'}, buf); }; + OAUTH3.crypto.core.pbkdf2 = deferCryptoCall("pbkdf2"); webCrypto.pbkdf2 = function (password, salt) { return OAUTH3._browser.window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey']) .then(function (key) { @@ -32,12 +56,15 @@ }); }; + OAUTH3.crypto.core.encrypt = deferCryptoCall("encrypt"); webCrypto.encrypt = function (rawKey, iv, data) { return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt']) .then(function (key) { return OAUTH3._browser.window.crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data); }); }; + + OAUTH3.crypto.core.decrypt = deferCryptoCall("decrypt"); webCrypto.decrypt = function (rawKey, iv, data) { return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt']) .then(function (key) { @@ -45,6 +72,7 @@ }); }; + OAUTH3.crypto.core.genEcdsaKeyPair = deferCryptoCall("genEcdsaKeyPair"); webCrypto.genEcdsaKeyPair = function () { return OAUTH3._browser.window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify']) .then(function (keyPair) { @@ -57,6 +85,7 @@ }); }; + OAUTH3.crypto.core.sign = deferCryptoCall("sign"); webCrypto.sign = function (jwk, msg) { return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign']) .then(function (key) { @@ -66,6 +95,8 @@ return new Uint8Array(sig); }); }; + + OAUTH3.crypto.core.verify = deferCryptoCall("verify"); webCrypto.verify = function (jwk, msg, signature) { // If the JWK has properties that should only exist on the private key or is missing // "verify" in the key_ops, importing in as a public key won't work. @@ -103,18 +134,19 @@ return prom; }; function checkException(name, func) { - OAUTH3.PromiseA.resolve().then(func) + return OAUTH3.PromiseA.resolve().then(func) .then(function () { OAUTH3.crypto.core[name] = webCrypto[name]; }, function (err) { console.warn('error with WebCrypto', name, '- using fallback', err); - loadFallback().then(function () { + return loadFallback().then(function () { OAUTH3.crypto.core[name] = OAUTH3_crypto_fallback[name]; }); }); } function checkResult(name, expected, func) { - checkException(name, function () { + + finishBeforeReady.push(checkException(name, function () { return func() .then(function (result) { if (typeof expected === typeof result) { @@ -127,7 +159,7 @@ throw new Error("result ("+result+") doesn't match expectation ("+expected+")"); } }); - }); + })); } var zeroBuf = new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]); @@ -159,12 +191,20 @@ return webCrypto.verify(jwk, dataBuf, sig); }); // The results of these functions are less predictable, so we can't check their return value. - checkException('genEcdsaKeyPair', function () { + finishBeforeReady.push(checkException('genEcdsaKeyPair', function () { return webCrypto.genEcdsaKeyPair(); - }); - checkException('sign', function () { + })); + finishBeforeReady.push(checkException('sign', function () { return webCrypto.sign(jwk, dataBuf); - }); + })); + + OAUTH3.PromiseA.all(finishBeforeReady) + .then(function(results) { + OAUTH3.crypto.core.ready = true; + deferedCalls.forEach(function(request) { + request(); + }); + }); } checkWebCrypto(); } From 0a980fd560185d47a3d26f95c8e48fb2311b91d0 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 14 Nov 2017 23:39:14 +0000 Subject: [PATCH 2/3] urls for otp --- oauth3.core.js | 2 +- oauth3.issuer.js | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/oauth3.core.js b/oauth3.core.js index 8e98e6d..276641e 100644 --- a/oauth3.core.js +++ b/oauth3.core.js @@ -706,7 +706,7 @@ */ return OAUTH3._browser.request(preq, opts); } - , implicitGrant: function(directives, opts) { + , implicitGrant: function (directives, opts) { var promise; var providerUri = directives.azp || directives.issuer || directives; diff --git a/oauth3.issuer.js b/oauth3.issuer.js index f70b0ae..0999403 100644 --- a/oauth3.issuer.js +++ b/oauth3.issuer.js @@ -287,21 +287,25 @@ OAUTH3.urls.publishKey = function (directive, opts) { , session: opts.session }; }; +OAUTH3.urls.credentialMeta = function (directive, opts) { + return OAUTH3.url.resolve(directive.api, directive.credential_meta.url) + .replace(':type', 'email') + .replace(':id', opts.email) +}; OAUTH3.authn = {}; OAUTH3.authn.loginMeta = function (directive, opts) { + var url = OAUTH3.urls.credentialMeta(directive, opts); return OAUTH3.request({ method: directive.credential_meta.method || 'GET' // TODO lint urls // TODO client_uri - , url: OAUTH3.url.resolve(directive.api, directive.credential_meta.url) - .replace(':type', 'email') - .replace(':id', opts.email) + , url: url }); }; -OAUTH3.authn.otp = function (directive, opts) { +OAUTH3.urls.otp = function (directive, opts) { // TODO client_uri - var preq = { + return { method: directive.credential_otp.method || 'POST' , url: OAUTH3.url.resolve(directive.api, directive.credential_otp.url) , data: { @@ -314,6 +318,9 @@ OAUTH3.authn.otp = function (directive, opts) { , username: opts.email } }; +}; +OAUTH3.authn.otp = function (directive, opts) { + var preq = OAUTH3.urls.otp(directive, opts); return OAUTH3.request(preq); }; From b7aa754c480f42013433b371c2b49d74fdeb1f79 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Thu, 16 Nov 2017 05:30:27 +0000 Subject: [PATCH 3/3] enable showing all grants --- oauth3.issuer.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/oauth3.issuer.js b/oauth3.issuer.js index 0999403..6d1519f 100644 --- a/oauth3.issuer.js +++ b/oauth3.issuer.js @@ -192,8 +192,9 @@ OAUTH3.urls.grants = function (directive, opts) { } 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') + .replace(/(:azp|:client_id)/g, !opts.all && OAUTH3.uri.normalize(opts.client_id || opts.client_uri) || '') + .replace(/\/\/$/, '/') // if there's a double slash due to the sub not existing ; var data = { client_id: opts.client_id @@ -432,7 +433,7 @@ OAUTH3.authz.grants = function (providerUri, opts) { } // 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) { + if (opts.all || ('GET' !== opts.method && 'POST' !== opts.method)) { return grants; }