Repository: auth0/auth0.js (js)

Issue:
Nonce does not match
Using the `8.0.1` version, I always get a `Nonce does not match` error when 
parsing the token returned by `redirect.loginWithCredentials`.

The issue occurs inside a VueJS2 app.

I have an authentication file creating a client:
```
const auth0 = new Auth0.WebAuth({
  domain: process.env.AUTH0_DOMAIN,
  clientID: process.env.AUTH0_CLIENT_ID,
  responseType: 'token',
  redirectUri: window.location.origin + '/'
})

// login
let login = (username, password) => {
  auth0.redirect.loginWithCredentials({
    connection: 'Username-Password-Authentication',
    email: username,
    password: password,
    scope: 'openid email'
  },
  function (err) {
    if (err) console.error('something went wrong: ' + err.message)
  })
}
```
When the application is called back, I try to get read the provided token:
```
let requiresAuth = (to, from, next) => {
  if (!checkAuth()) {
    console.log('auth failed ...')
    let result = auth0.parseHash(window.location.hash, (err, data) => {
      console.log(err || data, window.location.hash)
    })
    console.log('Result:')
    console.dir(result)
    if (result && result.idToken) {
      console.log('Authentication succeeded')
      localStorage.setItem(ID_TOKEN_KEY, result.idToken)
      next({path: '/'})
    } else {
      console.log('Redirect to login...')
      next({path: '/login'})
    }
  } else {
    next()
  }
}
```

The complete app can be found here: https://github.com/vitactiv/vitactiv-admin/tree/version/lock

*EDIT:* Seems that the README has an error. By checking in the code of the transaction Manager, `responseType` should match `id_token` and not `token` as written in the README.md.

Pull request:
Polyfill functions
Added polyfill for functions not supported in IE (now has support for IE9+ if you provide a nonce instead of using the autogenerated)
Removed unused jwt module since we added the idtoken verifier

Closes #288 and #286

Patch:
diff --git a/example/index.html b/example/index.html
index 4504832..01a1929 100644
--- a/example/index.html
+++ b/example/index.html
@@ -62,7 +62,7 @@
       if (err) {
         return this.dump(err, 'error');
       }
-      if (data.error) {
+      if (data && data.error) {
         return this.dump(data, 'error');
       }
       if (data) {
@@ -175,13 +175,20 @@
     });
 
     webAuth.parseHash(function(err, data) {
+      console.log(err,data);
       if (err) {
         return htmlConsole.dumpCallback(err);
       }
 
-      htmlConsole.dumpCallback(null, data);
+      if (data) {
+        htmlConsole.dumpCallback(null, data);
+
+        if (data.accessToken) {
+          webAuth.client.userInfo(data.accessToken, htmlConsole.dumpCallback.bind(htmlConsole));
+        }
+      }
+
       window.location.hash = '';
-      webAuth.client.userInfo(data.accessToken, htmlConsole.dumpCallback.bind(htmlConsole));
     });
 
     $('#clear-console').click(function () {
@@ -198,7 +205,7 @@
 
     $('.login-db').click(function (e) {
       e.preventDefault();
-      webAuth.redirect.login({
+      webAuth.redirect.loginWithCredentials({
         connection: 'tests',
         username: $('.login-username').val(),
         password: $('.login-password').val(),
@@ -216,7 +223,7 @@
     $('.popup-login-db').click(function (e) {
       e.preventDefault();
 
-      webAuth.popup.login({
+      webAuth.popup.loginWithCredentials({
         connection: 'tests',
         username: $('.popup-login-username').val(),
         password: $('.popup-login-password').val(),
@@ -235,28 +242,28 @@
         audience: 'urn:test'
       }, function(err, data) {
         htmlConsole.dumpCallback.bind(htmlConsole)(err, data);
-        this.webAuth.client.userInfo(data.access_token, htmlConsole.dumpCallback.bind(htmlConsole));
+        this.webAuth.client.userInfo(data.accessToken, htmlConsole.dumpCallback.bind(htmlConsole));
       });
     });
 
     $('.login-facebook').click(function (e) {
       e.preventDefault();
-      webAuth.login({ connection: 'facebook' });
+      webAuth.authorize({ connection: 'facebook' });
     });
 
     $('.login-hosted').click(function (e) {
       e.preventDefault();
-      webAuth.login({});
+      webAuth.authorize({});
     });
 
     $('.login-twitter').click(function (e) {
       e.preventDefault();
-      webAuth.login({ connection: 'twitter' });
+      webAuth.authorize({ connection: 'twitter' });
     });
 
     $('.login-github').click(function (e) {
       e.preventDefault();
-      webAuth.login({ connection: 'github' });
+      webAuth.authorize({ connection: 'github' });
     });
 
     $('.popup-login-facebook').click(function (e) {
diff --git a/package.json b/package.json
index 3929377..31eb88e 100644
--- a/package.json
+++ b/package.json
@@ -30,7 +30,7 @@
   "license": "MIT",
   "dependencies": {
     "base64-js": "^1.2.0",
-    "idtoken-verifier": "^1.0.0",
+    "idtoken-verifier": "^1.0.1",
     "superagent": "^3.3.1",
     "url-join": "^1.1.0",
     "winchan": "^0.1.4"
diff --git a/src/helper/assert.js b/src/helper/assert.js
index 64dfba7..1918573 100644
--- a/src/helper/assert.js
+++ b/src/helper/assert.js
@@ -23,7 +23,10 @@ function check(o, config, attributes) {
     variable(o, config.type, config.message);
   }
   if (config.type === 'object' && attributes) {
-    Object.keys(attributes).forEach(function (a) { // eslint-disable-line
+    var keys = Object.keys(attributes);
+
+    for (var index = 0; index < keys.length; index++ ) {
+      var a = keys[index];
       if (!attributes[a].optional || o[a]) {
         if (!attributes[a].condition || attributes[a].condition(o)) {
           attribute(o, a, attributes[a].type, attributes[a].message);
@@ -32,7 +35,8 @@ function check(o, config, attributes) {
           }
         }
       }
-    });
+    }
+
   }
 }
 
diff --git a/src/helper/jwt.js b/src/helper/jwt.js
deleted file mode 100644
index 4852d9f..0000000
--- a/src/helper/jwt.js
+++ /dev/null
@@ -1,10 +0,0 @@
-var base64Url = require('./base64_url');
-
-function getPayload(jwt) {
-  var encoded = jwt && jwt.split('.')[1];
-  return JSON.parse(base64Url.decode(encoded));
-}
-
-module.exports = {
-  getPayload: getPayload
-};
diff --git a/src/helper/object-assign.js b/src/helper/object-assign.js
new file mode 100644
index 0000000..1c14342
--- /dev/null
+++ b/src/helper/object-assign.js
@@ -0,0 +1,37 @@
+function get() {
+  if (!Object.assign) {
+    return objectAssignPolyfill;
+  }
+
+  return Object.assign;
+}
+
+function objectAssignPolyfill(target) {
+  'use strict';
+  if (target === undefined || target === null) {
+    throw new TypeError('Cannot convert first argument to object');
+  }
+
+  var to = Object(target);
+  for (var i = 1; i < arguments.length; i++) {
+    var nextSource = arguments[i];
+    if (nextSource === undefined || nextSource === null) {
+      continue;
+    }
+
+    var keysArray = Object.keys(Object(nextSource));
+    for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
+      var nextKey = keysArray[nextIndex];
+      var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
+      if (desc !== undefined && desc.enumerable) {
+        to[nextKey] = nextSource[nextKey];
+      }
+    }
+  }
+  return to;
+}
+
+module.exports = {
+  get: get,
+  objectAssignPolyfill: objectAssignPolyfill
+};
\ No newline at end of file
diff --git a/src/helper/object.js b/src/helper/object.js
index 8b1d857..2c4d33d 100644
--- a/src/helper/object.js
+++ b/src/helper/object.js
@@ -1,5 +1,7 @@
 /* eslint-disable no-param-reassign */
 
+var objectAssign = require('./object-assign');
+
 function pick(object, keys) {
   return keys.reduce(function (prev, key) {
     if (object[key]) {
@@ -9,10 +11,18 @@ function pick(object, keys) {
   }, {});
 }
 
+function objectValues(obj) {
+  var values = [];
+  for (key in obj) {
+    values.push(obj[key]);
+  }
+  return values;
+}
+
 function extend() {
-  var params = Array.from(arguments);
+  var params = objectValues(arguments);
   params.unshift({});
-  return Object.assign.apply(undefined, params);
+  return objectAssign.get().apply(undefined, params);
 }
 
 function merge(object, keys) {
diff --git a/src/helper/random.js b/src/helper/random.js
index ab93701..84cdb6e 100644
--- a/src/helper/random.js
+++ b/src/helper/random.js
@@ -12,9 +12,9 @@ function randomString(length) {
 
   var random = cryptoObj.getRandomValues(bytes);
 
-  random.forEach(function (c) {
-    result.push(charset[c % charset.length]);
-  });
+  for (var a = 0; a < random.length; a++) {
+    result.push(charset[random[a] % charset.length]);
+  }
 
   return result.join('');
 }
diff --git a/src/helper/request-builder.js b/src/helper/request-builder.js
index c314d33..6aca455 100644
--- a/src/helper/request-builder.js
+++ b/src/helper/request-builder.js
@@ -76,9 +76,13 @@ RequestBuilder.prototype.setCommonConfiguration = function (ongoingRequest, opti
 
   var headers = this.headers;
   ongoingRequest = ongoingRequest.set('Content-Type', 'application/json');
-  Object.keys(this.headers).forEach(function (header) {
-    ongoingRequest = ongoingRequest.set(header, headers[header]);
-  });
+
+  var keys = Object.keys(this.headers);
+
+  for (var a = 0; a < keys.length; a++) {
+    ongoingRequest = ongoingRequest.set(keys[a], headers[keys[a]]);
+  }
+
   if (this._sendTelemetry) {
     ongoingRequest = ongoingRequest.set('Auth0-Client', this.getTelemetryData());
   }
diff --git a/src/web-auth/index.js b/src/web-auth/index.js
index 924055f..9560b8c 100644
--- a/src/web-auth/index.js
+++ b/src/web-auth/index.js
@@ -2,7 +2,6 @@ var IdTokenVerifier = require('idtoken-verifier');
 
 var assert = require('../helper/assert');
 var error = require('../helper/error');
-var jwt = require('../helper/jwt');
 var qs = require('../helper/qs');
 var windowHelper = require('../helper/window');
 var objectHelper = require('../helper/object');
diff --git a/src/web-auth/redirect.js b/src/web-auth/redirect.js
index a2a1208..cc21a70 100644
--- a/src/web-auth/redirect.js
+++ b/src/web-auth/redirect.js
@@ -1,5 +1,4 @@
 var UsernamePassword = require('./username-password');
-var TransactionManager = require('./transaction-manager');
 var objectHelper = require('../helper/object');
 var Warn = require('../helper/warn');
 var assert = require('../helper/assert');
@@ -8,7 +7,6 @@ function Redirect(client, options) {
   this.baseOptions = options;
   this.client = client;
 
-  this.transactionManager = new TransactionManager(this.baseOptions.transaction);
   this.warn = new Warn({
     disableWarnings: !!options._disableDeprecationWarnings
   });
@@ -41,8 +39,6 @@ Redirect.prototype.loginWithCredentials = function (options, cb) {
     responseType: { type: 'string', message: 'responseType option is required' }
   });
 
-  params = this.transactionManager.process(params);
-
   usernamePassword = new UsernamePassword(this.baseOptions);
   return usernamePassword.login(params, function (err, data) {
     if (err) {
diff --git a/src/web-auth/transaction-manager.js b/src/web-auth/transaction-manager.js
index e7cff41..4ffa7be 100644
--- a/src/web-auth/transaction-manager.js
+++ b/src/web-auth/transaction-manager.js
@@ -20,7 +20,7 @@ TransactionManager.prototype.process = function (options) {
     return options;
   }
 
-  transaction = this.generateTransaction(options.appState, options.state);
+  transaction = this.generateTransaction(options.appState, options.state, options.nonce);
 
   options.state = transaction.state;
 
@@ -31,12 +31,12 @@ TransactionManager.prototype.process = function (options) {
   return options;
 };
 
-TransactionManager.prototype.generateTransaction = function (appState, state) {
+TransactionManager.prototype.generateTransaction = function (appState, state, nonce) {
   var transaction;
   var nonce;
 
   transaction = state || random.randomString(this.keyLength);
-  nonce = random.randomString(this.keyLength);
+  nonce = nonce || random.randomString(this.keyLength);
 
   storage.setItem(this.namespace + transaction, {
     nonce:nonce,


Test patch:
diff --git a/test/helper/object.test.js b/test/helper/object.test.js
index 4bf176d..be8aeab 100644
--- a/test/helper/object.test.js
+++ b/test/helper/object.test.js
@@ -1,5 +1,7 @@
 var expect = require('expect.js');
+var stub = require('sinon').stub;
 
+var objectAssign = require('../../src/helper/object-assign');
 var objectHelper = require('../../src/helper/object');
 
 describe('helpers', function () {
@@ -73,6 +75,41 @@ describe('helpers', function () {
       });
     });
 
+    it('shold merge objects attributes with polyfill', function () {
+
+      stub(objectAssign, 'get', function() {
+        return objectAssign.objectAssignPolyfill;
+      });
+
+      var object1 = {
+        attr1: 'attribute_1',
+        attr2: 'attribute_2'
+      };
+
+      var object2 = {
+        attr3: 'attribute_3'
+      };
+
+      var newObject = objectHelper.extend(object1, object2);
+
+      expect(newObject).to.eql({
+        attr1: 'attribute_1',
+        attr2: 'attribute_2',
+        attr3: 'attribute_3'
+      });
+
+      expect(object1).to.eql({
+        attr1: 'attribute_1',
+        attr2: 'attribute_2'
+      });
+
+      expect(object2).to.eql({
+        attr3: 'attribute_3'
+      });
+
+      objectAssign.get.restore();
+    });
+
     it('shold merge objects attributes and override the first object ones', function () {
       var object1 = {
         attr1: 'attribute_1',
diff --git a/test/web-auth/redirect.test.js b/test/web-auth/redirect.test.js
index 99c819e..a8a07fc 100644
--- a/test/web-auth/redirect.test.js
+++ b/test/web-auth/redirect.test.js
@@ -94,9 +94,7 @@ describe('auth0.WebAuth.redirect', function () {
             response_type: 'id_token',
             scope: 'openid',
             tenant: 'me',
-            username: 'me@example.com',
-            state: 'ABCDEFGHIJ',
-            nonce: 'ABCDEFGHIJ'
+            username: 'me@example.com'
           },
           headers: {
             'Content-Type': 'application/json',
@@ -176,7 +174,6 @@ describe('auth0.WebAuth.redirect', function () {
             response_type: 'token',
             scope: 'openid',
             tenant: 'me',
-            state: 'ABCDEFGHIJ',
             username: 'me@example.com'
           },
           headers: {
@@ -261,7 +258,6 @@ describe('auth0.WebAuth.redirect', function () {
               client_id: '...',
               connection: 'the_connection',
               password: '123456',
-              state: 'ABCDEFGHIJ',
               redirect_uri: 'http://page.com/callback',
               response_type: 'token',
               scope: 'openid',