Repository: auth0/node-auth0
Pull request #375 (2019-07-11 18:24:09)

Add flag to bypass id_token validation
### Changes

Adds `bypassIdTokenValidation` option to bypass the id_token validation if necessary. Customers with clients created a long time ago (~3y) might be impacted by our latest update that started validating all the incoming id_token's. To fix this, you can set this flag to true or rotate your client secret to make sure it's using the newest format.

### Testing

Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors. 

- [x] This change adds unit test coverage
- [ ] This change adds integration test coverage



Patch:
diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index 9ff263a3b..d294fd10f 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -13,12 +13,13 @@ var HS256_IGNORE_VALIDATION_MESSAGE =
  * @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.
- * @param  {String}              [options.clientSecret]          Default client Secret.
- * @param  {String}              [options.supportedAlgorithms]   Algorithms that your application expects to receive
+ * @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.
+ * @param  {String}              [options.clientSecret]              Default client Secret.
+ * @param  {String}              [options.supportedAlgorithms]       Algorithms that your application expects to receive
+ * @param  {Boolean}             [options.__bypassIdTokenValidation] Whether the id_token should be validated or not
  */
 var OAUthWithIDTokenValidation = function(oauth, options) {
   if (!oauth) {
@@ -34,6 +35,7 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
   }
 
   this.oauth = oauth;
+  this.__bypassIdTokenValidation = options.__bypassIdTokenValidation;
   this.clientId = options.clientId;
   this.clientSecret = options.clientSecret;
   this.domain = options.domain;
@@ -56,8 +58,11 @@ var OAUthWithIDTokenValidation = function(oauth, options) {
  * @return  {Promise|undefined}
  */
 OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
+  const _this = this;
   const createAndValidate = this.oauth.create(params, data).then(r => {
-    var _this = this;
+    if (_this.__bypassIdTokenValidation) {
+      return r;
+    }
     if (r.id_token) {
       function getKey(header, callback) {
         if (header.alg === 'HS256') {
diff --git a/src/auth/OAuthAuthenticator.js b/src/auth/OAuthAuthenticator.js
index 7aaa41465..6586b81ba 100644
--- a/src/auth/OAuthAuthenticator.js
+++ b/src/auth/OAuthAuthenticator.js
@@ -12,11 +12,12 @@ var OAUthWithIDTokenValidation = require('./OAUthWithIDTokenValidation');
  * @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.
+ * @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.
+ * @param  {Boolean}             [options.__bypassIdTokenValidation] Whether the id_token should be validated or not
  */
 var OAuthAuthenticator = function(options) {
   if (!options) {
diff --git a/src/auth/index.js b/src/auth/index.js
index c86921d57..bcbceff2c 100644
--- a/src/auth/index.js
+++ b/src/auth/index.js
@@ -37,11 +37,12 @@ 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   {String}  [options.supportedAlgorithms]   Algorithms that your application expects to receive
+ * @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
+ * @param  {Boolean}  [options.__bypassIdTokenValidation] Whether the id_token should be validated or not
  */
 var AuthenticationClient = function(options) {
   if (!options || typeof options !== 'object') {
@@ -61,7 +62,8 @@ var AuthenticationClient = function(options) {
       'Content-Type': 'application/json'
     },
     baseUrl: util.format(BASE_URL_FORMAT, options.domain),
-    supportedAlgorithms: options.supportedAlgorithms
+    supportedAlgorithms: options.supportedAlgorithms,
+    __bypassIdTokenValidation: options.__bypassIdTokenValidation
   };
 
   if (options.telemetry !== false) {


Test patch:
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index fe8caf1d7..c9b8ab2b7 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -77,6 +77,17 @@ describe('OAUthWithIDTokenValidation', function() {
       var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
       oauthWithValidation.create(PARAMS, DATA, done);
     });
+    it('Bypasses validation when options.__bypassIdTokenValidation is true', function(done) {
+      var oauth = {
+        create: function() {
+          return new Promise(res => res({ id_token: 'foobar' }));
+        }
+      };
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {
+        __bypassIdTokenValidation: true
+      });
+      oauthWithValidation.create(PARAMS, DATA, done);
+    });
     it('Calls jwt.verify with token and algs', function(done) {
       var oauth = {
         create: function() {