Repository: auth0/node-auth0
Pull request #330 (2019-01-31 17:51:52)

Don't validate id_token when alg is HS256 and there is no clientSecret
fix https://github.com/auth0/node-auth0/issues/329

### Changes

In 2.13.0 we added ID token validation, but there are old tenants that return HS256 id_tokens. Since this library doesn't require the `clientSecret` param and the client secret is needed to validate HS256 id_tokens, the validation fails with an exception. This PR bypasses the validation when the id_token is HS256 and there is no client secret (it will emit a console.warn).

### References

#295 

### 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

### Checklist

[x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)

[x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)

[x] All existing and new tests complete without errors



Patch:
diff --git a/src/auth/OAUthWithIDTokenValidation.js b/src/auth/OAUthWithIDTokenValidation.js
index b9833f948..9ff263a3b 100644
--- a/src/auth/OAUthWithIDTokenValidation.js
+++ b/src/auth/OAUthWithIDTokenValidation.js
@@ -4,6 +4,9 @@ var Promise = require('bluebird');
 
 var ArgumentError = require('rest-facade').ArgumentError;
 
+var HS256_IGNORE_VALIDATION_MESSAGE =
+  'Validation of `id_token` requires a `clientSecret` when using the HS256 algorithm. To ensure tokens are validated, please switch the signing algorithm to RS256 or provide a `clientSecret` in the constructor.';
+
 /**
  * @class
  * Abstracts the `oauth.create` method with additional id_token validation
@@ -58,6 +61,9 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
     if (r.id_token) {
       function getKey(header, callback) {
         if (header.alg === 'HS256') {
+          if (!_this.clientSecret) {
+            return callback({ message: HS256_IGNORE_VALIDATION_MESSAGE });
+          }
           return callback(null, Buffer.from(_this.clientSecret, 'base64'));
         }
         _this._jwksClient.getSigningKey(header.kid, function(err, key) {
@@ -77,11 +83,15 @@ OAUthWithIDTokenValidation.prototype.create = function(params, data, cb) {
             audience: this.clientId,
             issuer: 'https://' + this.domain + '/'
           },
-          function(err, payload) {
-            if (err) {
-              return rej(err);
+          function(err) {
+            if (!err) {
+              return res(r);
+            }
+            if (err.message && err.message.includes(HS256_IGNORE_VALIDATION_MESSAGE)) {
+              console.warn(HS256_IGNORE_VALIDATION_MESSAGE);
+              return res(r);
             }
-            return res(r);
+            return rej(err);
           }
         );
       });


Test patch:
diff --git a/test/auth/oauth-with-idtoken-validation.tests.js b/test/auth/oauth-with-idtoken-validation.tests.js
index 8e53a2490..2f8dc4fb4 100644
--- a/test/auth/oauth-with-idtoken-validation.tests.js
+++ b/test/auth/oauth-with-idtoken-validation.tests.js
@@ -128,7 +128,7 @@ describe('OAUthWithIDTokenValidation', function() {
         done();
       });
     });
-    it('Calls uses secret as key when header.alg === HS256', function(done) {
+    it('Uses `clientSecret` as key when header.alg === HS256 and there is a user secret', function(done) {
       var oauth = {
         create: function() {
           return new Promise(res => res({ id_token: 'foobar' }));
@@ -145,6 +145,27 @@ describe('OAUthWithIDTokenValidation', function() {
       });
       oauthWithValidation.create(PARAMS, DATA);
     });
+    it('Returns unvalidated response when header.alg === HS256 and there is no user secret', 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(err.message).to.contain(
+            'Validation of `id_token` requires a `clientSecret` when using the HS256 algorithm. To ensure tokens are validated, please switch the signing algorithm to RS256 or provide a `clientSecret` in the constructor.'
+          );
+          callback(err, key);
+        });
+      });
+      var oauthWithValidation = new OAUthWithIDTokenValidation(oauth, {});
+      oauthWithValidation.create(PARAMS, DATA, function(err, response) {
+        expect(err).to.be.null;
+        expect(response).to.be.eql({ id_token: 'foobar' });
+        done();
+      });
+    });
     describe('when header.alg !== HS256', function() {
       it('creates a jwksClient with the correct jwksUri', function(done) {
         var oauth = {