From bcf80974cd9e8d99bc21239aedd4e0975935301b Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Wed, 22 Aug 2018 15:15:13 -0300
Subject: [PATCH 01/10] Adding id_token validation

---
 package.json                           |   2 +
 src/auth/OAUthWithIDTokenValidation.js |  90 +++++++++
 src/auth/OAuthAuthenticator.js         |  16 +-
 yarn.lock                              | 268 +++++++++++++++++++++++++
 4 files changed, 370 insertions(+), 6 deletions(-)
 create mode 100644 src/auth/OAUthWithIDTokenValidation.js

diff --git a/package.json b/package.json
index 60c49d951..bfbcccee2 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,8 @@
   "homepage": "https://github.com/auth0/node-auth0",
   "dependencies": {
     "bluebird": "^2.10.2",
+    "jsonwebtoken": "^8.3.0",
+    "jwks-rsa": "^1.3.0",
     "lru-memoizer": "^1.11.1",
     "object.assign": "^4.0.4",
     "request": "^2.83.0",
diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
new file mode 100644
index 000000000..fb5a92596
--- /dev/null
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -0,0 +1,90 @@
+var jwt = require('jsonwebtoken');
+var jwksClient = require('jwks-rsa');
+var Promise = require('bluebird');
+
+var ArgumentError = require('rest-facade').ArgumentError;
+
+/**
+ * @class
+ * Abstracts the `oauth.create` method with additional id_token validation\
+ * @constructor
+ * @memberOf module:auth
+ *
+ * @param  {Object}              options                Authenticator options.
+ * @param  {String}              options.baseUrl        The auth0 account URL.
+ * @param  {String}              options.domain       AuthenticationClient server domain
+ * @param  {String}              [options.clientId]     Default client ID.
+ * @param  {String}              [options.clientSecret] Default client Secret.
+ */
+var OAUthWithIDTokenValidation = function(oauth, options) {
+  if (!oauth) {
+    throw new ArgumentError('Missing authenticator options');
+  }
+
+  if (!options) {
+    throw new ArgumentError('Missing authenticator options');
+  }
+
+  if (typeof options !== 'object') {
+    throw new ArgumentError('The authenticator options must be an object');
+  }
+
+  this.oauth = oauth;
+  this.clientId = options.clientId;
+  this.clientSecret = options.clientSecret;
+  this.domain = options.domain;
+};
+
+/**
+ * Creates an oauth request
+ *
+ * @method    create
+ * @memberOf  module:auth.OAuthWithIDTokenValidation.prototype
+ *
+ * @param   {Object}    options               Options are passed through
+ * @param   {Function}  [callbabck]           Callback function
+ *
+ * @return  {Promise|undefined}
+ */
+OAUthWithIDTokenValidation.prototype.create = function(params, data, callback) {
+  const createAndValidate = this.oauth.create(params, data).then(r => {
+    var _this = this;
+    if (r.id_token) {
+      var client = jwksClient({
+        jwksUri: 'https://' + this.domain + '/.well-known/jwks.json'
+      });
+      function getKey(header, callback) {
+        if (header.alg === 'HS256') {
+          return callback(null, Buffer.from(_this.clientSecret, 'base64'));
+        }
+        client.getSigningKey(header.kid, function(err, key) {
+          var signingKey = key.publicKey || key.rsaPublicKey;
+          callback(err, signingKey);
+        });
+      }
+
+      return new Promise((res, rej) => {
+        jwt.verify(
+          r.id_token,
+          getKey,
+          {
+            algorithms: ['HS256', 'RS256']
+          },
+          function(err, payload) {
+            if (err) {
+              return rej(err);
+            }
+            return res(r);
+          }
+        );
+      });
+    }
+    return r;
+  });
+  if (!callback) {
+    return createAndValidate;
+  }
+  createAndValidate.then(r => callback(null, r)).catch(e => callback(e));
+};
+
+module.exports = OAUthWithIDTokenValidation;
diff --git a/src/auth/OAuthAuthenticator.js b/src/auth/OAuthAuthenticator.js
index 0af66bdf1..0b0005d56 100644
--- a/src/auth/OAuthAuthenticator.js
+++ b/src/auth/OAuthAuthenticator.js
@@ -3,6 +3,8 @@ var extend = require('util')._extend;
 var ArgumentError = require('rest-facade').ArgumentError;
 var RestClient = require('rest-facade').Client;
 
+var OAUthWithIDTokenValidation = require('./OAUthWithIDTokenValidation');
+
 /**
  * @class
  * Abstracts the sign-in, sign-up and change-password processes for Database &
@@ -12,6 +14,7 @@ var RestClient = require('rest-facade').Client;
  *
  * @param  {Object}              options                Authenticator options.
  * @param  {String}              options.baseUrl        The auth0 account URL.
+ * @param  {String}              options.domain       AuthenticationClient server domain
  * @param  {String}              [options.clientId]     Default client ID.
  * @param  {String}              [options.clientSecret] Default client Secret.
  */
@@ -34,6 +37,7 @@ var OAuthAuthenticator = function(options) {
   };
 
   this.oauth = new RestClient(options.baseUrl + '/oauth/:type', clientOptions);
+  this.oauthWithIDTokenValidation = new OAUthWithIDTokenValidation(this.oauth, options);
   this.clientId = options.clientId;
   this.clientSecret = options.clientSecret;
 };
@@ -96,10 +100,10 @@ OAuthAuthenticator.prototype.signIn = function(userData, cb) {
   }
 
   if (cb && cb instanceof Function) {
-    return this.oauth.create(params, data, cb);
+    return this.oauthWithIDTokenValidation.create(params, data, cb);
   }
 
-  return this.oauth.create(params, data);
+  return this.oauthWithIDTokenValidation.create(params, data);
 };
 
 /**
@@ -169,10 +173,10 @@ OAuthAuthenticator.prototype.passwordGrant = function(userData, cb) {
   }
 
   if (cb && cb instanceof Function) {
-    return this.oauth.create(params, data, cb);
+    return this.oauthWithIDTokenValidation.create(params, data, cb);
   }
 
-  return this.oauth.create(params, data);
+  return this.oauthWithIDTokenValidation.create(params, data);
 };
 
 /**
@@ -306,10 +310,10 @@ OAuthAuthenticator.prototype.authorizationCodeGrant = function(options, cb) {
   }
 
   if (cb && cb instanceof Function) {
-    return this.oauth.create(params, data, cb);
+    return this.oauthWithIDTokenValidation.create(params, data, cb);
   }
 
-  return this.oauth.create(params, data);
+  return this.oauthWithIDTokenValidation.create(params, data);
 };
 
 module.exports = OAuthAuthenticator;
diff --git a/yarn.lock b/yarn.lock
index 638ad7960..86991ea5d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,71 @@
 # yarn lockfile v1
 
 
+"@types/body-parser@*":
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.17.0.tgz#9f5c9d9bd04bb54be32d5eb9fc0d8c974e6cf58c"
+  dependencies:
+    "@types/connect" "*"
+    "@types/node" "*"
+
+"@types/connect@*":
+  version "3.4.32"
+  resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28"
+  dependencies:
+    "@types/node" "*"
+
+"@types/events@*":
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
+
+"@types/express-jwt@0.0.34":
+  version "0.0.34"
+  resolved "https://registry.yarnpkg.com/@types/express-jwt/-/express-jwt-0.0.34.tgz#fdbee4c6af5c0a246ef2a933f5519973c7717f02"
+  dependencies:
+    "@types/express" "*"
+    "@types/express-unless" "*"
+
+"@types/express-serve-static-core@*":
+  version "4.16.0"
+  resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.16.0.tgz#fdfe777594ddc1fe8eb8eccce52e261b496e43e7"
+  dependencies:
+    "@types/events" "*"
+    "@types/node" "*"
+    "@types/range-parser" "*"
+
+"@types/express-unless@*":
+  version "0.0.32"
+  resolved "https://registry.yarnpkg.com/@types/express-unless/-/express-unless-0.0.32.tgz#783f3cc1fa5e67cc2ed30000f3e1f22501f75d50"
+  dependencies:
+    "@types/express" "*"
+
+"@types/express@*":
+  version "4.16.0"
+  resolved "https://registry.yarnpkg.com/@types/express/-/express-4.16.0.tgz#6d8bc42ccaa6f35cf29a2b7c3333cb47b5a32a19"
+  dependencies:
+    "@types/body-parser" "*"
+    "@types/express-serve-static-core" "*"
+    "@types/serve-static" "*"
+
+"@types/mime@*":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.0.tgz#5a7306e367c539b9f6543499de8dd519fac37a8b"
+
+"@types/node@*":
+  version "10.7.1"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-10.7.1.tgz#b704d7c259aa40ee052eec678758a68d07132a2e"
+
+"@types/range-parser@*":
+  version "1.2.2"
+  resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.2.tgz#fa8e1ad1d474688a757140c91de6dace6f4abc8d"
+
+"@types/serve-static@*":
+  version "1.13.2"
+  resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.2.tgz#f5ac4d7a6420a99a6a45af4719f4dcd8cd907a48"
+  dependencies:
+    "@types/express-serve-static-core" "*"
+    "@types/mime" "*"
+
 abbrev@1, abbrev@1.0.x:
   version "1.0.9"
   resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
@@ -39,6 +104,15 @@ ajv@^5.1.0:
     json-schema-traverse "^0.3.0"
     json-stable-stringify "^1.0.1"
 
+ajv@^5.3.0:
+  version "5.5.2"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
+  dependencies:
+    co "^4.6.0"
+    fast-deep-equal "^1.0.0"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.3.0"
+
 align-text@^0.1.1, align-text@^0.1.3:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -169,6 +243,10 @@ aws4@^1.2.1, aws4@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
 
+aws4@^1.8.0:
+  version "1.8.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
+
 babylon@7.0.0-beta.19:
   version "7.0.0-beta.19"
   resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503"
@@ -254,6 +332,10 @@ browserify-zlib@^0.1.4:
   dependencies:
     pako "~0.2.0"
 
+buffer-equal-constant-time@1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
+
 buffer@^4.9.0:
   version "4.9.1"
   resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
@@ -409,6 +491,12 @@ color-name@^1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
 
+combined-stream@1.0.6, combined-stream@~1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
+  dependencies:
+    delayed-stream "~1.0.0"
+
 combined-stream@^1.0.5, combined-stream@~1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
@@ -620,6 +708,12 @@ ecc-jsbn@~0.1.1:
   dependencies:
     jsbn "~0.1.0"
 
+ecdsa-sig-formatter@1.0.10:
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.10.tgz#1c595000f04a8897dfb85000892a0f4c33af86c3"
+  dependencies:
+    safe-buffer "^5.0.1"
+
 emojis-list@^2.0.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
@@ -730,6 +824,10 @@ extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
+extend@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+
 extglob@^0.3.1:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@@ -744,6 +842,10 @@ fast-deep-equal@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
 
+fast-json-stable-stringify@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
+
 fast-levenshtein@~2.0.4:
   version "2.0.6"
   resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
@@ -812,6 +914,14 @@ form-data@~2.1.1:
     combined-stream "^1.0.5"
     mime-types "^2.1.12"
 
+form-data@~2.3.2:
+  version "2.3.2"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "1.0.6"
+    mime-types "^2.1.12"
+
 formatio@1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
@@ -999,6 +1109,13 @@ har-validator@~5.0.3:
     ajv "^5.1.0"
     har-schema "^2.0.0"
 
+har-validator@~5.1.0:
+  version "5.1.0"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.0.tgz#44657f5688a22cfd4b72486e81b3a3fb11742c29"
+  dependencies:
+    ajv "^5.3.0"
+    har-schema "^2.0.0"
+
 has-ansi@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@@ -1358,6 +1475,20 @@ jsonpointer@^4.0.0:
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9"
 
+jsonwebtoken@^8.3.0:
+  version "8.3.0"
+  resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.3.0.tgz#056c90eee9a65ed6e6c72ddb0a1d325109aaf643"
+  dependencies:
+    jws "^3.1.5"
+    lodash.includes "^4.3.0"
+    lodash.isboolean "^3.0.3"
+    lodash.isinteger "^4.0.4"
+    lodash.isnumber "^3.0.3"
+    lodash.isplainobject "^4.0.6"
+    lodash.isstring "^4.0.1"
+    lodash.once "^4.0.0"
+    ms "^2.1.1"
+
 jsprim@^1.2.2:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918"
@@ -1367,6 +1498,32 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.3.6"
 
+jwa@^1.1.5:
+  version "1.1.6"
+  resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.1.6.tgz#87240e76c9808dbde18783cf2264ef4929ee50e6"
+  dependencies:
+    buffer-equal-constant-time "1.0.1"
+    ecdsa-sig-formatter "1.0.10"
+    safe-buffer "^5.0.1"
+
+jwks-rsa@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/jwks-rsa/-/jwks-rsa-1.3.0.tgz#f37d2a6815af17a3b2e5898ab2a41ad8c168b295"
+  dependencies:
+    "@types/express-jwt" "0.0.34"
+    debug "^2.2.0"
+    limiter "^1.1.0"
+    lru-memoizer "^1.6.0"
+    ms "^2.0.0"
+    request "^2.73.0"
+
+jws@^3.1.5:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/jws/-/jws-3.1.5.tgz#80d12d05b293d1e841e7cb8b4e69e561adcf834f"
+  dependencies:
+    jwa "^1.1.5"
+    safe-buffer "^5.0.1"
+
 kind-of@^3.0.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
@@ -1396,6 +1553,10 @@ levn@~0.3.0:
     prelude-ls "~1.1.2"
     type-check "~0.3.2"
 
+limiter@^1.1.0:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/limiter/-/limiter-1.1.3.tgz#32e2eb55b2324076943e5d04c1185ffb387968ef"
+
 loader-utils@^0.2.11, loader-utils@^0.2.5, loader-utils@~0.2.2, loader-utils@~0.2.3, loader-utils@~0.2.5:
   version "0.2.17"
   resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348"
@@ -1416,10 +1577,42 @@ lock@~0.1.2:
   version "0.1.4"
   resolved "https://registry.yarnpkg.com/lock/-/lock-0.1.4.tgz#fec7deaef17e7c3a0a55e1da042803e25d91745d"
 
+lodash.includes@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
+
+lodash.isboolean@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
+
+lodash.isinteger@^4.0.4:
+  version "4.0.4"
+  resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343"
+
+lodash.isnumber@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc"
+
+lodash.isplainobject@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+
+lodash.isstring@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+
+lodash.once@^4.0.0:
+  version "4.1.1"
+  resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
+
 lodash@2.4.1:
   version "2.4.1"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.1.tgz#5b7723034dda4d262e5a46fb2c58d7cc22f71420"
 
+lodash@^4.17.4:
+  version "4.17.10"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
+
 lodash@~4.5.1:
   version "4.5.1"
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.5.1.tgz#80e8a074ca5f3893a6b1c10b2a636492d710c316"
@@ -1473,6 +1666,15 @@ lru-memoizer@^1.11.1:
     lru-cache "~4.0.0"
     very-fast-args "^1.1.0"
 
+lru-memoizer@^1.6.0:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/lru-memoizer/-/lru-memoizer-1.12.0.tgz#efe65706cc8a9cc653f80f0d5a6ea38ad950e352"
+  dependencies:
+    lock "~0.1.2"
+    lodash "^4.17.4"
+    lru-cache "~4.0.0"
+    very-fast-args "^1.1.0"
+
 marked@~0.3.6:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
@@ -1526,6 +1728,10 @@ mime-db@~1.30.0:
   version "1.30.0"
   resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
 
+mime-db@~1.35.0:
+  version "1.35.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.35.0.tgz#0569d657466491283709663ad379a99b90d9ab47"
+
 mime-types@^2.1.12, mime-types@~2.1.7:
   version "2.1.15"
   resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
@@ -1538,6 +1744,12 @@ mime-types@~2.1.17:
   dependencies:
     mime-db "~1.30.0"
 
+mime-types@~2.1.19:
+  version "2.1.19"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.19.tgz#71e464537a7ef81c15f2db9d97e913fc0ff606f0"
+  dependencies:
+    mime-db "~1.35.0"
+
 mime@^1.4.1:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
@@ -1626,6 +1838,10 @@ ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
 
+ms@^2.0.0, ms@^2.1.1:
+  version "2.1.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a"
+
 nan@^2.3.0:
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
@@ -1734,6 +1950,10 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
   version "0.8.2"
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
 
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+
 object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -1968,6 +2188,10 @@ pseudomap@^1.0.1, pseudomap@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
 
+psl@^1.1.24:
+  version "1.1.29"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67"
+
 punycode@1.3.2:
   version "1.3.2"
   resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -1988,6 +2212,10 @@ qs@~6.4.0:
   version "6.4.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
 
+qs@~6.5.2:
+  version "6.5.2"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
+
 querystring-es3@^0.2.0:
   version "0.2.1"
   resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -2107,6 +2335,31 @@ request@2.79.0:
     tunnel-agent "~0.4.1"
     uuid "^3.0.0"
 
+request@^2.73.0:
+  version "2.88.0"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.0"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.4.3"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
 request@^2.81.0:
   version "2.81.0"
   resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
@@ -2209,6 +2462,10 @@ safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
 
+safe-buffer@^5.1.2:
+  version "5.1.2"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+
 safe-buffer@~5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
@@ -2568,6 +2825,13 @@ tough-cookie@~2.3.3:
   dependencies:
     punycode "^1.4.1"
 
+tough-cookie@~2.4.3:
+  version "2.4.3"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"
+  dependencies:
+    psl "^1.1.24"
+    punycode "^1.4.1"
+
 tty-browserify@0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
@@ -2666,6 +2930,10 @@ uuid@^3.0.0, uuid@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
 
+uuid@^3.3.2:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
+
 verror@1.3.6:
   version "1.3.6"
   resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"

From b77df7143a9ad01894c337b9f43e2173062cd140 Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Thu, 23 Aug 2018 16:59:58 -0300
Subject: [PATCH 02/10] tesssssts

---
 package.json                                  |   1 +
 src/auth/OAUthWithIDTokenValidation.js        |  18 +-
 .../oauth-with-idtoken-validation.tests.js    | 264 ++++++++++++++++++
 test/auth/oauth.tests.js                      |  33 +++
 yarn.lock                                     |  37 +++
 5 files changed, 345 insertions(+), 8 deletions(-)
 create mode 100644 test/auth/oauth-with-idtoken-validation.tests.js

diff --git a/package.json b/package.json
index bfbcccee2..f0b1c605d 100644
--- a/package.json
+++ b/package.json
@@ -53,6 +53,7 @@
     "nock": "^3.1.1",
     "prettier": "^1.12.0",
     "pretty-quick": "^1.4.1",
+    "proxyquire": "^2.1.0",
     "sinon": "^1.17.1",
     "string-replace-webpack-plugin": "0.0.3",
     "webpack": "^1.12.14"
diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index fb5a92596..8ec9c4b23 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -46,20 +46,22 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
  *
  * @return  {Promise|undefined}
  */
-OAUthWithIDTokenValidation.prototype.create = function(params, data, callback) {
+OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
   const createAndValidate = this.oauth.create(params, data).then(r => {
     var _this = this;
     if (r.id_token) {
-      var client = jwksClient({
-        jwksUri: 'https://' + this.domain + '/.well-known/jwks.json'
-      });
       function getKey(header, callback) {
         if (header.alg === 'HS256') {
           return callback(null, Buffer.from(_this.clientSecret, 'base64'));
         }
-        client.getSigningKey(header.kid, function(err, key) {
+        jwksClient({
+          jwksUri: 'https://' + _this.domain + '/.well-known/jwks.json'
+        }).getSigningKey(header.kid, function(err, key) {
+          if (err) {
+            return callback(err);
+          }
           var signingKey = key.publicKey || key.rsaPublicKey;
-          callback(err, signingKey);
+          callback(null, signingKey);
         });
       }
 
@@ -81,10 +83,10 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, callback) {
     }
     return r;
   });
-  if (!callback) {
+  if (!cb) {
     return createAndValidate;
   }
-  createAndValidate.then(r => callback(null, r)).catch(e => callback(e));
+  createAndValidate.then(r => cb(null, r)).catch(e => cb(e));
 };
 
 module.exports = OAUthWithIDTokenValidation;
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
new file mode 100644
index 000000000..2f6aa5865
--- /dev/null
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -0,0 +1,264 @@
+var expect = require('chai').expect;
+var Promise = require('bluebird');
+var sinon = require('sinon');
+var proxyquire = require('proxyquire');
+
+var jwt = require('jsonwebtoken');
+var jwksClient = require('jwks-rsa');
+
+// Constants.
+var DOMAIN = 'tenant.auth0.com';
+var CLIENT_ID = 'TEST_CLIENT_ID';
+var CLIENT_SECRET = new Buffer('TEST_CLIENT_SECRET', 'base64');
+
+var OAUthWithIDTokenValidation = require('../../src/auth/OAUthWithIDTokenValidation');
+var PARAMS = { params: true };
+var DATA = { data: true };
+
+describe('OAUthWithIDTokenValidation2', function() {
+  describe('#create', function() {
+    this.afterEach(function() {
+      if (jwt.verify.restore) {
+        jwt.verify.restore();
+      }
+      if (jwksClient.restore) {
+        jwksClient.restore();
+      }
+    });
+    it('Calls `oauth.create` with correct params', function(done) {
+      var oauth = {
+        create: function(params, data) {
+          expect(params).to.be.equal(PARAMS);
+          expect(data).to.be.equal(DATA);
+          return new Promise(res => res({}));
+        }
+      };
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA, done);
+    });
+    it('Does nothing when there is no id_token', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({}));
+        }
+      };
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA, done);
+    });
+    it('Calls jwt.verify with token and algs', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({ id_token: 'foobar' }));
+        }
+      };
+      sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+        expect(idtoken).to.be.equal('foobar');
+        expect(options).to.be.eql({
+          algorithms: ['HS256', 'RS256']
+        });
+        done();
+      });
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA);
+    });
+    it('Returns auth result when verify response is successful', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({ id_token: 'foobar' }));
+        }
+      };
+      sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+        callback(null, { verification: 'result' });
+      });
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA).then(function(r) {
+        expect(r).to.be.eql({ id_token: 'foobar' });
+        done();
+      });
+    });
+    it('Returns error when verify response is an error', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({ id_token: 'foobar' }));
+        }
+      };
+      sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+        callback({ the: 'error' });
+      });
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA).catch(function(r) {
+        expect(r).to.be.eql({ the: 'error' });
+        done();
+      });
+    });
+    it('Calls uses secret as key when header.alg === HS256', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({ id_token: 'foobar' }));
+        }
+      };
+      sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+        getKey({ alg: 'HS256' }, function(err, key) {
+          expect(key).to.be.eql(Buffer.from(CLIENT_SECRET, 'base64'));
+          done();
+        });
+      });
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+        clientSecret: CLIENT_SECRET
+      });
+      oauthWithValidation.create(PARAMS, DATA);
+    });
+    describe('when header.alg !== HS256', function() {
+      it('creates a jwksClient with the correct jwksUri', function(done) {
+        var oauth = {
+          create: function() {
+            return new Promise(res => res({ id_token: 'foobar' }));
+          }
+        };
+        var jwksClientStub = sinon.spy(function() {
+          return {
+            getSigningKey: function(kid, cb) {
+              cb(null, { publicKey: 'publicKey' });
+            }
+          };
+        });
+        OAUthWithIDTokenValidationProxy = proxyquire('../../src/auth/OAUthWithIDTokenValidation', {
+          'jwks-rsa': jwksClientStub
+        });
+
+        sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+          getKey({ alg: 'RS256' }, function(err, key) {
+            expect(jwksClientStub.getCall(0).args[0].jwksUri).to.be.equal(
+              'https://tenant.auth0.com/.well-known/jwks.json'
+            );
+            done();
+          });
+        });
+        var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+          domain: DOMAIN,
+          clientSecret: CLIENT_SECRET
+        });
+        oauthWithValidation.create(PARAMS, DATA);
+      });
+      it('returns the error when available', function(done) {
+        var oauth = {
+          create: function() {
+            return new Promise(res => res({ id_token: 'foobar' }));
+          }
+        };
+        var jwksClientStub = sinon.spy(function() {
+          return {
+            getSigningKey: function(kid, cb) {
+              cb({ the: 'error' });
+            }
+          };
+        });
+        OAUthWithIDTokenValidationProxy = proxyquire('../../src/auth/OAUthWithIDTokenValidation', {
+          'jwks-rsa': jwksClientStub
+        });
+
+        sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+          getKey({ kid: 'kid', alg: 'RS256' }, function(err, key) {
+            expect(err).to.be.eql({ the: 'error' });
+            done();
+          });
+        });
+        var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+          domain: DOMAIN,
+          clientSecret: CLIENT_SECRET
+        });
+        oauthWithValidation.create(PARAMS, DATA);
+      });
+      it('uses the publicKey when available', function(done) {
+        var oauth = {
+          create: function() {
+            return new Promise(res => res({ id_token: 'foobar' }));
+          }
+        };
+        var jwksClientStub = sinon.spy(function() {
+          return {
+            getSigningKey: function(kid, cb) {
+              expect(kid).to.be.equal('kid');
+              cb(null, { publicKey: 'publicKey' });
+            }
+          };
+        });
+        OAUthWithIDTokenValidationProxy = proxyquire('../../src/auth/OAUthWithIDTokenValidation', {
+          'jwks-rsa': jwksClientStub
+        });
+
+        sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+          getKey({ kid: 'kid', alg: 'RS256' }, function(err, key) {
+            expect(key).to.be.equal('publicKey');
+            done();
+          });
+        });
+        var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+          domain: DOMAIN,
+          clientSecret: CLIENT_SECRET
+        });
+        oauthWithValidation.create(PARAMS, DATA);
+      });
+      it('uses the publicKey when both keys (publicKey and rsaPublicKey) available', function(done) {
+        var oauth = {
+          create: function() {
+            return new Promise(res => res({ id_token: 'foobar' }));
+          }
+        };
+        var jwksClientStub = sinon.spy(function() {
+          return {
+            getSigningKey: function(kid, cb) {
+              expect(kid).to.be.equal('kid');
+              cb(null, { publicKey: 'publicKey', rsaPublicKey: 'rsaPublicKey' });
+            }
+          };
+        });
+        OAUthWithIDTokenValidationProxy = proxyquire('../../src/auth/OAUthWithIDTokenValidation', {
+          'jwks-rsa': jwksClientStub
+        });
+
+        sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+          getKey({ kid: 'kid', alg: 'RS256' }, function(err, key) {
+            expect(key).to.be.equal('publicKey');
+            done();
+          });
+        });
+        var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+          domain: DOMAIN,
+          clientSecret: CLIENT_SECRET
+        });
+        oauthWithValidation.create(PARAMS, DATA);
+      });
+      it('uses the rsaPublicKey when there is no publicKey available', function(done) {
+        var oauth = {
+          create: function() {
+            return new Promise(res => res({ id_token: 'foobar' }));
+          }
+        };
+        var jwksClientStub = sinon.spy(function() {
+          return {
+            getSigningKey: function(kid, cb) {
+              expect(kid).to.be.equal('kid');
+              cb(null, { rsaPublicKey: 'rsaPublicKey' });
+            }
+          };
+        });
+        OAUthWithIDTokenValidationProxy = proxyquire('../../src/auth/OAUthWithIDTokenValidation', {
+          'jwks-rsa': jwksClientStub
+        });
+
+        sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
+          getKey({ kid: 'kid', alg: 'RS256' }, function(err, key) {
+            expect(key).to.be.equal('rsaPublicKey');
+            done();
+          });
+        });
+        var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+          domain: DOMAIN,
+          clientSecret: CLIENT_SECRET
+        });
+        oauthWithValidation.create(PARAMS, DATA);
+      });
+    });
+  });
+});
diff --git a/test/auth/oauth.tests.js b/test/auth/oauth.tests.js
index 03e652f1d..3dcb436a2 100644
--- a/test/auth/oauth.tests.js
+++ b/test/auth/oauth.tests.js
@@ -2,6 +2,7 @@ var expect = require('chai').expect;
 var extend = require('util')._extend;
 var nock = require('nock');
 var Promise = require('bluebird');
+var sinon = require('sinon');
 
 // Constants.
 var SRC_DIR = '../../src';
@@ -12,6 +13,7 @@ var CLIENT_SECRET = 'TEST_CLIENT_SECRET';
 
 var ArgumentError = require('rest-facade').ArgumentError;
 var Authenticator = require(SRC_DIR + '/auth/OAuthAuthenticator');
+var OAUthWithIDTokenValidation = require('../../src/auth/OAUthWithIDTokenValidation');
 
 var validOptions = {
   baseUrl: API_URL,
@@ -20,7 +22,11 @@ var validOptions = {
 };
 
 describe('OAuthAuthenticator', function() {
+  beforeEach(function() {
+    sinon.spy(OAUthWithIDTokenValidation.prototype, 'create');
+  });
   afterEach(function() {
+    OAUthWithIDTokenValidation.prototype.create.restore();
     nock.cleanAll();
   });
 
@@ -218,6 +224,15 @@ describe('OAuthAuthenticator', function() {
         })
         .catch(done);
     });
+    it('should use OAUthWithIDTokenValidation', function(done) {
+      this.authenticator
+        .signIn(userData)
+        .then(function() {
+          expect(OAUthWithIDTokenValidation.prototype.create.calledOnce).to.be.true;
+          done();
+        })
+        .catch(done);
+    });
   });
 
   describe('#passwordGrant', function() {
@@ -379,6 +394,15 @@ describe('OAuthAuthenticator', function() {
         })
         .catch(done);
     });
+    it('should use OAUthWithIDTokenValidation', function(done) {
+      this.authenticator
+        .passwordGrant(userData)
+        .then(function() {
+          expect(OAUthWithIDTokenValidation.prototype.create.calledOnce).to.be.true;
+          done();
+        })
+        .catch(done);
+    });
   });
 
   describe('#socialSignIn', function() {
@@ -815,5 +839,14 @@ describe('OAuthAuthenticator', function() {
         })
         .catch(done);
     });
+    it('should use OAUthWithIDTokenValidation', function(done) {
+      this.authenticator
+        .authorizationCodeGrant(data)
+        .then(function() {
+          expect(OAUthWithIDTokenValidation.prototype.create.calledOnce).to.be.true;
+          done();
+        })
+        .catch(done);
+    });
   });
 });
diff --git a/yarn.lock b/yarn.lock
index 86991ea5d..e4c1a919b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -864,6 +864,13 @@ filename-regex@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
 
+fill-keys@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/fill-keys/-/fill-keys-1.0.2.tgz#9a8fa36f4e8ad634e3bf6b4f3c8882551452eb20"
+  dependencies:
+    is-object "~1.0.1"
+    merge-descriptors "~1.0.0"
+
 fill-range@^2.1.0:
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723"
@@ -1329,6 +1336,10 @@ is-number@^3.0.0:
   dependencies:
     kind-of "^3.0.2"
 
+is-object@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
+
 is-posix-bracket@^0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@@ -1698,6 +1709,10 @@ memory-fs@~0.3.0:
     errno "^0.1.3"
     readable-stream "^2.0.1"
 
+merge-descriptors@~1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+
 methods@^1.1.1:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
@@ -1822,6 +1837,10 @@ mocha@^2.2.4:
     supports-color "1.2.0"
     to-iso-string "0.0.2"
 
+module-not-found-error@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/module-not-found-error/-/module-not-found-error-1.0.1.tgz#cf8b4ff4f29640674d6cdd02b0e3bc523c2bbdc0"
+
 moment@^2.18.1:
   version "2.18.1"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.18.1.tgz#c36193dd3ce1c2eed2adb7c802dbbc77a81b1c0f"
@@ -2111,6 +2130,10 @@ path-key@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
 
+path-parse@^1.0.5:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
+
 pbkdf2-compat@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288"
@@ -2180,6 +2203,14 @@ proxy-agent@2:
     pac-proxy-agent "^2.0.0"
     socks-proxy-agent "2"
 
+proxyquire@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/proxyquire/-/proxyquire-2.1.0.tgz#c2263a38bf0725f2ae950facc130e27510edce8d"
+  dependencies:
+    fill-keys "^1.0.2"
+    module-not-found-error "^1.0.0"
+    resolve "~1.8.1"
+
 prr@~0.0.0:
   version "0.0.0"
   resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
@@ -2424,6 +2455,12 @@ resolve@1.1.x:
   version "1.1.7"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
 
+resolve@~1.8.1:
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26"
+  dependencies:
+    path-parse "^1.0.5"
+
 rest-facade@^1.10.1:
   version "1.10.1"
   resolved "https://registry.yarnpkg.com/rest-facade/-/rest-facade-1.10.1.tgz#a9b030ff50df28c9ea1a2719f94e369c47167d20"

From 8ba9738713f97fc140069c19aef7da216721e76e Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Wed, 12 Sep 2018 15:57:08 -0300
Subject: [PATCH 03/10] PR review

---
 src/auth/OAUthWithIDTokenValidation.js | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index 8ec9c4b23..3cb3108ba 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -6,19 +6,18 @@ var ArgumentError = require('rest-facade').ArgumentError;
 
 /**
  * @class
- * Abstracts the `oauth.create` method with additional id_token validation\
+ * Abstracts the `oauth.create` method with additional id_token validation
  * @constructor
  * @memberOf module:auth
  *
  * @param  {Object}              options                Authenticator options.
- * @param  {String}              options.baseUrl        The auth0 account URL.
- * @param  {String}              options.domain       AuthenticationClient server domain
+ * @param  {String}              options.domain         AuthenticationClient server domain
  * @param  {String}              [options.clientId]     Default client ID.
  * @param  {String}              [options.clientSecret] Default client Secret.
  */
 var OAUthWithIDTokenValidation = function(oauth, options) {
   if (!oauth) {
-    throw new ArgumentError('Missing authenticator options');
+    throw new ArgumentError('Missing OAuthAuthenticator param');
   }
 
   if (!options) {
@@ -36,13 +35,14 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
 };
 
 /**
- * Creates an oauth request
+ * Creates an oauth request and validates the id_token (if any)
  *
  * @method    create
  * @memberOf  module:auth.OAuthWithIDTokenValidation.prototype
  *
- * @param   {Object}    options               Options are passed through
- * @param   {Function}  [callbabck]           Callback function
+ * @param   {Object}    params            OAuth parameters that are passed through
+ * @param   {Object}    data              Custom parameters sent to the OAuth endpoint
+ * @param   {Function}  [callback]        Callback function
  *
  * @return  {Promise|undefined}
  */
@@ -61,7 +61,7 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
             return callback(err);
           }
           var signingKey = key.publicKey || key.rsaPublicKey;
-          callback(null, signingKey);
+          return callback(null, signingKey);
         });
       }
 

From 763ea3ce689519d61b22773131a7314c4394771a Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Wed, 12 Sep 2018 18:21:20 -0300
Subject: [PATCH 04/10] Adding supportedAlgorithms param + integration tests

---
 src/auth/OAUthWithIDTokenValidation.js        |  15 ++-
 src/auth/index.js                             |  13 +-
 .../oauth-with-idtoken-validation.tests.js    | 118 +++++++++++++++++-
 3 files changed, 131 insertions(+), 15 deletions(-)

diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index 3cb3108ba..8d248d38d 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -10,10 +10,11 @@ var ArgumentError = require('rest-facade').ArgumentError;
  * @constructor
  * @memberOf module:auth
  *
- * @param  {Object}              options                Authenticator options.
- * @param  {String}              options.domain         AuthenticationClient server domain
- * @param  {String}              [options.clientId]     Default client ID.
- * @param  {String}              [options.clientSecret] Default client Secret.
+ * @param  {Object}              options                         Authenticator options.
+ * @param  {String}              options.domain                  AuthenticationClient server domain
+ * @param  {String}              [options.clientId]              Default client ID.
+ * @param  {String}              [options.clientSecret]          Default client Secret.
+ * @param  {String}              [options.supportedAlgorithms]   Algorithms that your application expects to receive
  */
 var OAUthWithIDTokenValidation = function(oauth, options) {
   if (!oauth) {
@@ -32,6 +33,7 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
   this.clientId = options.clientId;
   this.clientSecret = options.clientSecret;
   this.domain = options.domain;
+  this.supportedAlgorithms = options.supportedAlgorithms || ['HS256', 'RS256'];
 };
 
 /**
@@ -64,13 +66,14 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
           return callback(null, signingKey);
         });
       }
-
       return new Promise((res, rej) => {
         jwt.verify(
           r.id_token,
           getKey,
           {
-            algorithms: ['HS256', 'RS256']
+            algorithms: this.supportedAlgorithms,
+            audience: this.clientId,
+            issuer: 'https://' + this.domain + '/'
           },
           function(err, payload) {
             if (err) {
diff --git a/src/auth/index.js b/src/auth/index.js
index 56df17e83..fa410aa9c 100644
--- a/src/auth/index.js
+++ b/src/auth/index.js
@@ -39,11 +39,11 @@ var BASE_URL_FORMAT = 'https://%s';
  *   clientId: '{OPTIONAL_CLIENT_ID}'
  * });
  *
- * @param   {Object}  options                 Options for the Authentication Client
- *                                            SDK.
- * @param   {String}  options.domain          AuthenticationClient server domain.
- * @param   {String}  [options.clientId]      Default client ID.
- * @param   {String}  [options.clientSecret]  Default client Secret.
+ * @param   {Object}  options                         Options for the Authentication Client SDK.
+ * @param   {String}  options.domain                  AuthenticationClient server domain.
+ * @param   {String}  [options.clientId]              Default client ID.
+ * @param   {String}  [options.clientSecret]          Default client Secret.
+ * @param   {String}  [options.supportedAlgorithms]   Algorithms that your application expects to receive
  */
 var AuthenticationClient = function(options) {
   if (!options || typeof options !== 'object') {
@@ -62,7 +62,8 @@ var AuthenticationClient = function(options) {
       'User-agent': 'node.js/' + process.version.replace('v', ''),
       'Content-Type': 'application/json'
     },
-    baseUrl: util.format(BASE_URL_FORMAT, options.domain)
+    baseUrl: util.format(BASE_URL_FORMAT, options.domain),
+    supportedAlgorithms: options.supportedAlgorithms
   };
 
   if (options.telemetry !== false) {
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 2f6aa5865..28c7b8c1a 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -15,7 +15,7 @@ var OAUthWithIDTokenValidation = require('../../src/auth/OAUthWithIDTokenValidat
 var PARAMS = { params: true };
 var DATA = { data: true };
 
-describe('OAUthWithIDTokenValidation2', function() {
+describe('OAUthWithIDTokenValidation', function() {
   describe('#create', function() {
     this.afterEach(function() {
       if (jwt.verify.restore) {
@@ -54,11 +54,16 @@ describe('OAUthWithIDTokenValidation2', function() {
       sinon.stub(jwt, 'verify', function(idtoken, getKey, options, callback) {
         expect(idtoken).to.be.equal('foobar');
         expect(options).to.be.eql({
-          algorithms: ['HS256', 'RS256']
+          audience: CLIENT_ID,
+          algorithms: ['HS256', 'RS256'],
+          issuer: 'https://' + DOMAIN + '/'
         });
         done();
       });
-      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+        clientId: CLIENT_ID,
+        domain: DOMAIN
+      });
       oauthWithValidation.create(PARAMS, DATA);
     });
     it('Returns auth result when verify response is successful', function(done) {
@@ -260,5 +265,112 @@ describe('OAUthWithIDTokenValidation2', function() {
         oauthWithValidation.create(PARAMS, DATA);
       });
     });
+    describe('#integration', function() {
+      it('with a HS256 id_token and `options.supportedAlgorithms===RS256`', done => {
+        var oauth = {
+          create: function() {
+            return new Promise(res =>
+              res({
+                id_token:
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMDo1MjoxMS4zMDZaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9hdXRoLmJydWNrZS5jbHViLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg1NTMxLCJleHAiOjE1MzY4MjE1MzF9.mZGsJyJYyp_mkINcnV0JRJ6QPsTXUE8FrpRTruAIqhE'
+              })
+            );
+          }
+        };
+        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+          clientSecret: CLIENT_SECRET,
+          supportedAlgorithms: ['RS256']
+        });
+        oauthWithValidation.create(PARAMS, DATA, function(e) {
+          expect(e.message).to.eq('invalid algorithm');
+          done();
+        });
+      });
+      it('when `token.header.alg===RS256` and `options.supportedAlgorithms===HS256`', done => {
+        var oauth = {
+          create: function() {
+            return new Promise(res =>
+              res({
+                id_token:
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMDo1NTozMi4xMTlaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9hdXRoLmJydWNrZS5jbHViLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg1NzMyLCJleHAiOjE1MzY4MjE3MzJ9.i8iBJntBiSPRLIJdLmgTwnT_FXamc4ug8al8Ws1X-P7UAxbEaaa3irjqfBnDf50tDAQkHFcwIKiMDIrEHHBEPPEc7MH8dlxDAr80Pr8-T-M_ls8U6KccBGfrsurlJaU6qMVSfUP25kmZm5torI0D81c9rZRWcdpb64EnZCvqpUPWZjap__PoC-G88NRH_28jT_hV-bGYgbjJ3FqL_xTZ2u866bQljt1oJlOf3vvLIL4tW9MYdYxOvh7VZXWji9TirrjCb6cuq-CZ5ZWTSpV_NRC24BMdGx_Mu-4EBUMb8uWiaLBrjJgb_NtOZXY6p6PeJQuX5S2MeD2z_SCXOcwukQ'
+              })
+            );
+          }
+        };
+        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+          clientSecret: CLIENT_SECRET,
+          domain: 'brucke.auth0.com',
+          supportedAlgorithms: ['HS256']
+        });
+        oauthWithValidation.create(PARAMS, DATA, function(e) {
+          expect(e.message).to.eq('invalid algorithm');
+          done();
+        });
+      });
+      it('when `token.aud` is invalid', done => {
+        var oauth = {
+          create: function() {
+            return new Promise(res =>
+              res({
+                id_token:
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMDo1NTozMi4xMTlaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9hdXRoLmJydWNrZS5jbHViLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg1NzMyLCJleHAiOjE1MzY4MjE3MzJ9.i8iBJntBiSPRLIJdLmgTwnT_FXamc4ug8al8Ws1X-P7UAxbEaaa3irjqfBnDf50tDAQkHFcwIKiMDIrEHHBEPPEc7MH8dlxDAr80Pr8-T-M_ls8U6KccBGfrsurlJaU6qMVSfUP25kmZm5torI0D81c9rZRWcdpb64EnZCvqpUPWZjap__PoC-G88NRH_28jT_hV-bGYgbjJ3FqL_xTZ2u866bQljt1oJlOf3vvLIL4tW9MYdYxOvh7VZXWji9TirrjCb6cuq-CZ5ZWTSpV_NRC24BMdGx_Mu-4EBUMb8uWiaLBrjJgb_NtOZXY6p6PeJQuX5S2MeD2z_SCXOcwukQ'
+              })
+            );
+          }
+        };
+        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+          clientId: 'foobar',
+          clientSecret: CLIENT_SECRET,
+          domain: 'brucke.auth0.com',
+          supportedAlgorithms: ['RS256']
+        });
+        oauthWithValidation.create(PARAMS, DATA, function(e) {
+          expect(e.message).to.eq('jwt audience invalid. expected: foobar');
+          done();
+        });
+      });
+      it('when `token.iss` is invalid', done => {
+        var oauth = {
+          create: function() {
+            return new Promise(res =>
+              res({
+                id_token:
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMToxMzo1OS42OTNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg2ODM5LCJleHAiOjE1MzY4MjI4Mzl9.BkWn4lTu-_GWBC9QfjN1fux4yNe7TfjoyVwsd6tWc7GpuIsAb6GtZbiijfVBkiCchp7V28U2APMTi5Wt1luKkdD0OgI28GOFKKp6qM1qbpt1kexr3So5TgfTb9xbQF-B2HCrqE-fGughAOD4qc4N4UPS_6vRz24fyb4Y8O1wPtdfg9h49ioDa-c3-gyYsaWtUqRJfFoVU9AXBZaIJnKoefz5Oz-_cRWxOI5ci_zWWE2BoxkIMqWVN3Xzzr2njdFKM22HrIIzCz23neNW7bzyMotGjG0B4dbKaY4oIGiK7nI4OopDKbK1AD8KZpTjw97SpL8MbnIhN8-1c1fIVOxKEA'
+              })
+            );
+          }
+        };
+        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+          clientSecret: CLIENT_SECRET,
+          domain: 'auth.brucke.club',
+          supportedAlgorithms: ['RS256']
+        });
+        oauthWithValidation.create(PARAMS, DATA, function(e, d) {
+          expect(e.message).to.eq('jwt issuer invalid. expected: https://auth.brucke.club/');
+          done();
+        });
+      });
+      it('when `token.exp` is expired', done => {
+        var oauth = {
+          create: function() {
+            return new Promise(res =>
+              res({
+                id_token:
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMToxNzoyNy41NjVaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg3MDQ3LCJleHAiOjE1MzY3ODcwNTJ9.Wn6ie7sXQ7jG94MDumSa2vciKkt5qrDN8LGWw1U9cz8Oh15JxFZOxtPJxWST5t6i8biJ4l7fvjez7KkoibRf9TPXpe0VxE2SsQCy-H2TRlUSnodBg25WRPPKmXvA6tB_CeaZjDplaTV21fnvcRq7kCwl_O91meWS7Qs3rEWvrD_M63LvDPvAReKcNFRg42p_nZS5fnq2CLC6OHUBznkZfMforNJ8YC0GufcrBd2lRaNljF57Z6fHSupfwY9vLIxfp-nx7yYl7H1vjp75f-08h8mOLRgZdpCjG3z8QKCBwsY_5t8dnQfZiUsGhRFx6hsTb6BC35JHkNHSyOw75tfl9A'
+              })
+            );
+          }
+        };
+        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+          clientSecret: CLIENT_SECRET,
+          domain: 'auth.brucke.club',
+          supportedAlgorithms: ['RS256']
+        });
+        oauthWithValidation.create(PARAMS, DATA, function(e, d) {
+          expect(e.message).to.eq('jwt expired');
+          done();
+        });
+      });
+    });
   });
 });

From 872195266a89c9adf9bc10bd426f45b76c655b0c Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Thu, 20 Sep 2018 17:03:50 -0300
Subject: [PATCH 05/10] simple PR fixes

---
 src/auth/OAUthWithIDTokenValidation.js           |  1 +
 src/auth/OAuthAuthenticator.js                   |  4 ++--
 test/auth/oauth-with-idtoken-validation.tests.js | 12 ++++++------
 3 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index 8d248d38d..3d8c66a06 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -10,6 +10,7 @@ var ArgumentError = require('rest-facade').ArgumentError;
  * @constructor
  * @memberOf module:auth
  *
+ * @param  {Object}              oauth                           An instance of @type {OAuthAuthenticator}
  * @param  {Object}              options                         Authenticator options.
  * @param  {String}              options.domain                  AuthenticationClient server domain
  * @param  {String}              [options.clientId]              Default client ID.
diff --git a/src/auth/OAuthAuthenticator.js b/src/auth/OAuthAuthenticator.js
index 0b0005d56..0d2b33d81 100644
--- a/src/auth/OAuthAuthenticator.js
+++ b/src/auth/OAuthAuthenticator.js
@@ -13,8 +13,8 @@ var OAUthWithIDTokenValidation = require('./OAUthWithIDTokenValidation');
  * @memberOf module:auth
  *
  * @param  {Object}              options                Authenticator options.
- * @param  {String}              options.baseUrl        The auth0 account URL.
- * @param  {String}              options.domain       AuthenticationClient server domain
+ * @param  {String}              options.baseUrl        The Auth0 account URL.
+ * @param  {String}              options.domain         AuthenticationClient server domain
  * @param  {String}              [options.clientId]     Default client ID.
  * @param  {String}              [options.clientSecret] Default client Secret.
  */
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 28c7b8c1a..13da6fd6c 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -15,7 +15,7 @@ var OAUthWithIDTokenValidation = require('../../src/auth/OAUthWithIDTokenValidat
 var PARAMS = { params: true };
 var DATA = { data: true };
 
-describe('OAUthWithIDTokenValidation', function() {
+describe.only('OAUthWithIDTokenValidation', function() {
   describe('#create', function() {
     this.afterEach(function() {
       if (jwt.verify.restore) {
@@ -266,7 +266,7 @@ describe('OAUthWithIDTokenValidation', function() {
       });
     });
     describe('#integration', function() {
-      it('with a HS256 id_token and `options.supportedAlgorithms===RS256`', done => {
+      it('fails with a HS256 id_token and `options.supportedAlgorithms===RS256`', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>
@@ -286,7 +286,7 @@ describe('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      it('when `token.header.alg===RS256` and `options.supportedAlgorithms===HS256`', done => {
+      it('fails with a RS256 id_token and `options.supportedAlgorithms===HS256`', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>
@@ -307,7 +307,7 @@ describe('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      it('when `token.aud` is invalid', done => {
+      it('fails when `token.aud` is invalid', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>
@@ -329,7 +329,7 @@ describe('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      it('when `token.iss` is invalid', done => {
+      it('fails when `token.iss` is invalid', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>
@@ -350,7 +350,7 @@ describe('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      it('when `token.exp` is expired', done => {
+      it('fails when `token.exp` is expired', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>

From 16c1751b4ecebf1e460d532f8c9121173413fb81 Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Fri, 21 Sep 2018 15:13:42 -0300
Subject: [PATCH 06/10] Adding integration tests with a new certificate for
 each test

---
 package.json                                  |   1 +
 .../oauth-with-idtoken-validation.tests.js    | 141 ++++++++++++------
 yarn.lock                                     |  23 ++-
 3 files changed, 121 insertions(+), 44 deletions(-)

diff --git a/package.json b/package.json
index f0b1c605d..539b2e801 100644
--- a/package.json
+++ b/package.json
@@ -51,6 +51,7 @@
     "mocha-multi": "^0.11.0",
     "moment": "^2.18.1",
     "nock": "^3.1.1",
+    "pem": "^1.13.1",
     "prettier": "^1.12.0",
     "pretty-quick": "^1.4.1",
     "proxyquire": "^2.1.0",
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 13da6fd6c..2063823cd 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -5,6 +5,7 @@ var proxyquire = require('proxyquire');
 
 var jwt = require('jsonwebtoken');
 var jwksClient = require('jwks-rsa');
+var pem = require('pem');
 
 // Constants.
 var DOMAIN = 'tenant.auth0.com';
@@ -15,6 +16,20 @@ var OAUthWithIDTokenValidation = require('../../src/auth/OAUthWithIDTokenValidat
 var PARAMS = { params: true };
 var DATA = { data: true };
 
+var createCertificate = function(cb) {
+  pem.createCertificate({ days: 1, selfSigned: true }, function(err, keys) {
+    if (err) {
+      throw err;
+    }
+    pem.getPublicKey(keys.certificate, function(e, p) {
+      if (e) {
+        throw e;
+      }
+      cb({ serviceKey: keys.serviceKey, certificate: keys.certificate, publicKey: p.publicKey });
+    });
+  });
+};
+
 describe.only('OAUthWithIDTokenValidation', function() {
   describe('#create', function() {
     this.afterEach(function() {
@@ -307,35 +322,13 @@ describe.only('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      it('fails when `token.aud` is invalid', done => {
-        var oauth = {
-          create: function() {
-            return new Promise(res =>
-              res({
-                id_token:
-                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMDo1NTozMi4xMTlaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9hdXRoLmJydWNrZS5jbHViLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg1NzMyLCJleHAiOjE1MzY4MjE3MzJ9.i8iBJntBiSPRLIJdLmgTwnT_FXamc4ug8al8Ws1X-P7UAxbEaaa3irjqfBnDf50tDAQkHFcwIKiMDIrEHHBEPPEc7MH8dlxDAr80Pr8-T-M_ls8U6KccBGfrsurlJaU6qMVSfUP25kmZm5torI0D81c9rZRWcdpb64EnZCvqpUPWZjap__PoC-G88NRH_28jT_hV-bGYgbjJ3FqL_xTZ2u866bQljt1oJlOf3vvLIL4tW9MYdYxOvh7VZXWji9TirrjCb6cuq-CZ5ZWTSpV_NRC24BMdGx_Mu-4EBUMb8uWiaLBrjJgb_NtOZXY6p6PeJQuX5S2MeD2z_SCXOcwukQ'
-              })
-            );
-          }
-        };
-        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
-          clientId: 'foobar',
-          clientSecret: CLIENT_SECRET,
-          domain: 'brucke.auth0.com',
-          supportedAlgorithms: ['RS256']
-        });
-        oauthWithValidation.create(PARAMS, DATA, function(e) {
-          expect(e.message).to.eq('jwt audience invalid. expected: foobar');
-          done();
-        });
-      });
-      it('fails when `token.iss` is invalid', done => {
+      it('fails when `token.exp` is expired', done => {
         var oauth = {
           create: function() {
             return new Promise(res =>
               res({
                 id_token:
-                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMToxMzo1OS42OTNaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg2ODM5LCJleHAiOjE1MzY4MjI4Mzl9.BkWn4lTu-_GWBC9QfjN1fux4yNe7TfjoyVwsd6tWc7GpuIsAb6GtZbiijfVBkiCchp7V28U2APMTi5Wt1luKkdD0OgI28GOFKKp6qM1qbpt1kexr3So5TgfTb9xbQF-B2HCrqE-fGughAOD4qc4N4UPS_6vRz24fyb4Y8O1wPtdfg9h49ioDa-c3-gyYsaWtUqRJfFoVU9AXBZaIJnKoefz5Oz-_cRWxOI5ci_zWWE2BoxkIMqWVN3Xzzr2njdFKM22HrIIzCz23neNW7bzyMotGjG0B4dbKaY4oIGiK7nI4OopDKbK1AD8KZpTjw97SpL8MbnIhN8-1c1fIVOxKEA'
+                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMToxNzoyNy41NjVaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg3MDQ3LCJleHAiOjE1MzY3ODcwNTJ9.Wn6ie7sXQ7jG94MDumSa2vciKkt5qrDN8LGWw1U9cz8Oh15JxFZOxtPJxWST5t6i8biJ4l7fvjez7KkoibRf9TPXpe0VxE2SsQCy-H2TRlUSnodBg25WRPPKmXvA6tB_CeaZjDplaTV21fnvcRq7kCwl_O91meWS7Qs3rEWvrD_M63LvDPvAReKcNFRg42p_nZS5fnq2CLC6OHUBznkZfMforNJ8YC0GufcrBd2lRaNljF57Z6fHSupfwY9vLIxfp-nx7yYl7H1vjp75f-08h8mOLRgZdpCjG3z8QKCBwsY_5t8dnQfZiUsGhRFx6hsTb6BC35JHkNHSyOw75tfl9A'
               })
             );
           }
@@ -346,29 +339,93 @@ describe.only('OAUthWithIDTokenValidation', function() {
           supportedAlgorithms: ['RS256']
         });
         oauthWithValidation.create(PARAMS, DATA, function(e, d) {
-          expect(e.message).to.eq('jwt issuer invalid. expected: https://auth.brucke.club/');
+          expect(e.message).to.eq('jwt expired');
           done();
         });
       });
-      it('fails when `token.exp` is expired', done => {
-        var oauth = {
-          create: function() {
-            return new Promise(res =>
-              res({
-                id_token:
-                  'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FVkJOVU5CT1RneFJrRTVOa1F6UXpjNE9UQkVNRUZGUkRRNU4wUTJRamswUmtRMU1qRkdNUSJ9.eyJuaWNrbmFtZSI6ImpvaG5mb28iLCJuYW1lIjoiam9obmZvb0BnbWFpbC5jb20iLCJwaWN0dXJlIjoiaHR0cHM6Ly9zLmdyYXZhdGFyLmNvbS9hdmF0YXIvMzhmYTAwMjQyM2JkOGM5NDFjNmVkMDU4OGI2MGZmZWQ_cz00ODAmcj1wZyZkPWh0dHBzJTNBJTJGJTJGY2RuLmF1dGgwLmNvbSUyRmF2YXRhcnMlMkZqby5wbmciLCJ1cGRhdGVkX2F0IjoiMjAxOC0wOS0xMlQyMToxNzoyNy41NjVaIiwiZW1haWwiOiJqb2huZm9vQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiaXNzIjoiaHR0cHM6Ly9icnVja2UuYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDVhMjA1NGZmNDUxNTc3MTFiZTgxODJmNCIsImF1ZCI6IkVxbjdHTUV3VzhDbmN1S2FhcFRuNWs5VEJ0MzRQdldmIiwiaWF0IjoxNTM2Nzg3MDQ3LCJleHAiOjE1MzY3ODcwNTJ9.Wn6ie7sXQ7jG94MDumSa2vciKkt5qrDN8LGWw1U9cz8Oh15JxFZOxtPJxWST5t6i8biJ4l7fvjez7KkoibRf9TPXpe0VxE2SsQCy-H2TRlUSnodBg25WRPPKmXvA6tB_CeaZjDplaTV21fnvcRq7kCwl_O91meWS7Qs3rEWvrD_M63LvDPvAReKcNFRg42p_nZS5fnq2CLC6OHUBznkZfMforNJ8YC0GufcrBd2lRaNljF57Z6fHSupfwY9vLIxfp-nx7yYl7H1vjp75f-08h8mOLRgZdpCjG3z8QKCBwsY_5t8dnQfZiUsGhRFx6hsTb6BC35JHkNHSyOw75tfl9A'
-              })
+      describe('when using a valid token', function() {
+        it('fails when `token.aud` is invalid', done => {
+          createCertificate(function(c) {
+            var idtoken = jwt.sign({ foo: 'bar' }, c.serviceKey, {
+              algorithm: 'RS256',
+              audience: 'wrong_audience',
+              expiresIn: '1h'
+            });
+            var oauth = {
+              create: function() {
+                return new Promise(res =>
+                  res({
+                    id_token: idtoken
+                  })
+                );
+              }
+            };
+            var jwksClientStub = sinon.spy(function() {
+              return {
+                getSigningKey: function(kid, cb) {
+                  cb(null, { publicKey: c.publicKey });
+                }
+              };
+            });
+            OAUthWithIDTokenValidationProxy = proxyquire(
+              '../../src/auth/OAUthWithIDTokenValidation',
+              {
+                'jwks-rsa': jwksClientStub
+              }
             );
-          }
-        };
-        var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
-          clientSecret: CLIENT_SECRET,
-          domain: 'auth.brucke.club',
-          supportedAlgorithms: ['RS256']
+            var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+              clientId: 'foobar',
+              clientSecret: CLIENT_SECRET,
+              domain: 'brucke.auth0.com',
+              supportedAlgorithms: ['RS256']
+            });
+            oauthWithValidation.create(PARAMS, DATA, function(e) {
+              expect(e.message).to.eq('jwt audience invalid. expected: foobar');
+              done();
+            });
+          });
         });
-        oauthWithValidation.create(PARAMS, DATA, function(e, d) {
-          expect(e.message).to.eq('jwt expired');
-          done();
+        it('fails when `token.iss` is invalid', done => {
+          createCertificate(function(c) {
+            var idtoken = jwt.sign({ foo: 'bar' }, c.serviceKey, {
+              algorithm: 'RS256',
+              issuer: 'wrong_issuer',
+              audience: 'foobar',
+              expiresIn: '1h'
+            });
+            var oauth = {
+              create: function() {
+                return new Promise(res =>
+                  res({
+                    id_token: idtoken
+                  })
+                );
+              }
+            };
+            var jwksClientStub = sinon.spy(function() {
+              return {
+                getSigningKey: function(kid, cb) {
+                  cb(null, { publicKey: c.publicKey });
+                }
+              };
+            });
+            OAUthWithIDTokenValidationProxy = proxyquire(
+              '../../src/auth/OAUthWithIDTokenValidation',
+              {
+                'jwks-rsa': jwksClientStub
+              }
+            );
+            var oauthWithValidation = new OAUthWithIDTokenValidationProxy(oauth, {
+              clientId: 'foobar',
+              clientSecret: CLIENT_SECRET,
+              domain: 'brucke.auth0.com',
+              supportedAlgorithms: ['RS256']
+            });
+            oauthWithValidation.create(PARAMS, DATA, function(e) {
+              expect(e.message).to.eq('jwt issuer invalid. expected: https://brucke.auth0.com/');
+              done();
+            });
+          });
         });
       });
     });
diff --git a/yarn.lock b/yarn.lock
index e4c1a919b..8531b7c3c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -742,6 +742,10 @@ es6-promisify@^5.0.0:
   dependencies:
     es6-promise "^4.0.3"
 
+es6-promisify@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-6.0.0.tgz#b526a75eaa5ca600e960bf3d5ad98c40d75c7203"
+
 escape-string-regexp@1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz#4dbc2fe674e71949caf3fb2695ce7f2dc1d9a8d1"
@@ -1690,7 +1694,7 @@ marked@~0.3.6:
   version "0.3.6"
   resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.6.tgz#b2c6c618fccece4ef86c4fc6cb8a7cbf5aeda8d7"
 
-md5@^2.1.0:
+md5@^2.1.0, md5@^2.2.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
   dependencies:
@@ -2028,7 +2032,7 @@ os-homedir@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
 
-os-tmpdir@^1.0.0:
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
 
@@ -2138,6 +2142,15 @@ pbkdf2-compat@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz#b6e0c8fa99494d94e0511575802a59a5c142f288"
 
+pem@^1.13.1:
+  version "1.13.1"
+  resolved "https://registry.yarnpkg.com/pem/-/pem-1.13.1.tgz#57dd3e0c044fbcf709db026a737e1aad7dc8330f"
+  dependencies:
+    es6-promisify "^6.0.0"
+    md5 "^2.2.1"
+    os-tmpdir "^1.0.1"
+    which "^1.3.1"
+
 performance-now@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5"
@@ -3034,6 +3047,12 @@ which@^1.2.9:
   dependencies:
     isexe "^2.0.0"
 
+which@^1.3.1:
+  version "1.3.1"
+  resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
+  dependencies:
+    isexe "^2.0.0"
+
 wide-align@^1.1.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"

From f9e69b9b54421d33380c1f4b331e793cd593c4ff Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Fri, 21 Sep 2018 15:45:03 -0300
Subject: [PATCH 07/10] remove .only

---
 test/auth/oauth-with-idtoken-validation.tests.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 2063823cd..84f4cba26 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -30,7 +30,7 @@ var createCertificate = function(cb) {
   });
 };
 
-describe.only('OAUthWithIDTokenValidation', function() {
+describe('OAUthWithIDTokenValidation', function() {
   describe('#create', function() {
     this.afterEach(function() {
       if (jwt.verify.restore) {

From 6d39e106db39b544ad610b1aaeee982f1119b76d Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Fri, 21 Sep 2018 18:04:15 -0300
Subject: [PATCH 08/10] renaming test section

---
 test/auth/oauth-with-idtoken-validation.tests.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 84f4cba26..4fc364bc7 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -343,7 +343,7 @@ describe('OAUthWithIDTokenValidation', function() {
           done();
         });
       });
-      describe('when using a valid token', function() {
+      describe('when using a valid certificate to generate an invalid id_token', function() {
         it('fails when `token.aud` is invalid', done => {
           createCertificate(function(c) {
             var idtoken = jwt.sign({ foo: 'bar' }, c.serviceKey, {
@@ -386,11 +386,12 @@ describe('OAUthWithIDTokenValidation', function() {
           });
         });
         it('fails when `token.iss` is invalid', done => {
+          const TEST_AUDIENCE = 'foobar';
           createCertificate(function(c) {
             var idtoken = jwt.sign({ foo: 'bar' }, c.serviceKey, {
               algorithm: 'RS256',
               issuer: 'wrong_issuer',
-              audience: 'foobar',
+              audience: TEST_AUDIENCE,
               expiresIn: '1h'
             });
             var oauth = {

From c514e61798b33dcf14ffbc495a480f25eff01175 Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Fri, 21 Sep 2018 18:32:55 -0300
Subject: [PATCH 09/10] Add missig tests

---
 .../auth/oauth-with-idtoken-validation.tests.js | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 4fc364bc7..8e53a2490 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -31,6 +31,23 @@ var createCertificate = function(cb) {
 };
 
 describe('OAUthWithIDTokenValidation', function() {
+  describe('constructor', function() {
+    it('validates `oauth` is required', function() {
+      expect(function() {
+        new OAUthWithIDTokenValidation();
+      }).to.throw('Missing OAuthAuthenticator param');
+    });
+    it('validates `options` is required', function() {
+      expect(function() {
+        new OAUthWithIDTokenValidation({});
+      }).to.throw('Missing authenticator options');
+    });
+    it('validates `oauth` is required', function() {
+      expect(function() {
+        new OAUthWithIDTokenValidation({}, 'asd');
+      }).to.throw('The authenticator options must be an object');
+    });
+  });
   describe('#create', function() {
     this.afterEach(function() {
       if (jwt.verify.restore) {

From 6d9f6b675e3e213640b9460c4221f029f68006c2 Mon Sep 17 00:00:00 2001
From: Luis Deschamps Rudge <luis@luisrudge.net>
Date: Mon, 24 Sep 2018 10:59:19 -0300
Subject: [PATCH 10/10] create jwksClient in the constructor

---
 src/auth/OAUthWithIDTokenValidation.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index 3d8c66a06..b9833f948 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -35,6 +35,9 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
   this.clientSecret = options.clientSecret;
   this.domain = options.domain;
   this.supportedAlgorithms = options.supportedAlgorithms || ['HS256', 'RS256'];
+  this._jwksClient = jwksClient({
+    jwksUri: 'https://' + options.domain + '/.well-known/jwks.json'
+  });
 };
 
 /**
@@ -57,9 +60,7 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
         if (header.alg === 'HS256') {
           return callback(null, Buffer.from(_this.clientSecret, 'base64'));
         }
-        jwksClient({
-          jwksUri: 'https://' + _this.domain + '/.well-known/jwks.json'
-        }).getSigningKey(header.kid, function(err, key) {
+        _this._jwksClient.getSigningKey(header.kid, function(err, key) {
           if (err) {
             return callback(err);
           }