Repository: auth0/auth0.js
Pull request #240 (2016-11-16 19:10:47)

Change webauth structure + Allow to abort requests


Patch:
diff --git a/example/index.html b/example/index.html
index fff7faa..564a7f7 100644
--- a/example/index.html
+++ b/example/index.html
@@ -12,6 +12,7 @@
       }
       pre {
         margin: 0 0 10px 0;
+        min-height: 300px;
       }
       code {
         min-height: 300px;
@@ -39,15 +40,40 @@
     <script type="text/javascript">
     function HTMLConsole(options) {
       this.ele = $(options.selector);
+      this.data = [];
+      var _this = this;
+      var data;
+      if (data = localStorage.getItem("consoleData")) {
+        data = JSON.parse(data);
+        data.forEach(function(d){
+          _this.dumpCallback(d.error ? d : null, d.error ? null : d);
+        });
+      }
     }
 
     HTMLConsole.prototype.clear = function () {
       this.ele.html('');
+      localStorage.removeItem("consoleData");
+    };
+
+    HTMLConsole.prototype.dumpCallback = function (err, data) {
+      if (err) {
+        return this.dump(err, 'error');
+      }
+      if (data.error) {
+        return this.dump(data, 'error');
+      }
+      if (data) {
+        return this.dump(data);
+      }
     };
 
     HTMLConsole.prototype.dump = function (o, className) {
       className = className || '';
 
+      this.data.push(o);
+      localStorage.setItem("consoleData", JSON.stringify(this.data));
+
       function replacer(key, value) {
         if (typeof value === 'object') {
           return value;
@@ -84,6 +110,13 @@
           <input type="button" class="login-db" value="login" />
         </div>
 
+        <div>
+          <h2>Login with database connection (client login):</h2>
+          <input class="client-login-username" value="johnfoo@gmail.com" />
+          <input type="password" class="client-login-password" value="1234" />
+          <input type="button" class="client-login-db" value="login" />
+        </div>
+
         <div>
           <h2>Login with social connections:</h2>
           <input type="button" class="login-facebook" value="Facebook" />
@@ -123,18 +156,20 @@
 
     var hash = webAuth.parseHash();
     if (hash) {
-      htmlConsole.dump(hash);
+      htmlConsole.dumpCallback(hash.error ? hash : null, hash.error ? null : hash);
       window.location.hash = '';
     }
 
     $('#clear-console').click(function () {
       $('#clear-console').removeClass('icon-budicon-498');
       $('#clear-console').addClass('icon-budicon-495');
+
       htmlConsole.clear();
+
       setTimeout(function () {
         $('#clear-console').removeClass('icon-budicon-495');
         $('#clear-console').addClass('icon-budicon-498');
-      }, 250)
+      }, 250);
     });
 
     $('.login-db').click(function (e) {
@@ -144,40 +179,43 @@
         username: $('.login-username').val(),
         password: $('.login-password').val(),
         scope: 'openid'
-      }, function (err) {
-        htmlConsole.dump({err: err, data: data}, 'error');
-      });
+      }, htmlConsole.dumpCallback.bind(htmlConsole));
+    });
+
+    $('.client-login-db').click(function (e) {
+      e.preventDefault();
+      webAuth.client.login({
+        connection: 'tests',
+        username: $('.client-login-username').val(),
+        password: $('.client-login-password').val(),
+        scope: 'openid'
+      }, htmlConsole.dumpCallback.bind(htmlConsole));
     });
 
     $('.login-facebook').click(function (e) {
       e.preventDefault();
-      webAuth.redirect.authorize({ connection: 'facebook' });
+      webAuth.login({ connection: 'facebook' });
     });
 
     $('.login-twitter').click(function (e) {
       e.preventDefault();
-      webAuth.redirect.authorize({ connection: 'twitter' });
+      webAuth.login({ connection: 'twitter' });
     });
 
     $('.login-github').click(function (e) {
       e.preventDefault();
-      webAuth.redirect.authorize({ connection: 'github' });
+      webAuth.login({ connection: 'github' });
     });
 
     $('.logout').click(function (e) {
       e.preventDefault();
-      webAuth.redirect.logout({ returnTo: 'http://localhost:3000/example' });
+      webAuth.logout({ returnTo: 'http://localhost:3000/example' });
     });
 
     $('.renew-auth').click(function (e) {
       e.preventDefault();
-      webAuth.renewAuth({usePostMessage: true, scope:'openid', audience:'https://auth0-tests-auth0js.auth0.com/api/v2/', redirectURI: 'http://localhost:3000/example/callback.html'}, function (err, data) {
-          if (err) {
-            htmlConsole.dump(err, 'error');
-          } else {
-            htmlConsole.dump(data);
-          }
-        }
+      webAuth.renewAuth({usePostMessage: true, scope:'openid', audience:'https://auth0-tests-auth0js.auth0.com/ api/v2/', redirectURI: 'http://localhost:3000/example/callback.html'},
+        htmlConsole.dumpCallback.bind(htmlConsole)
       );
     });
     </script>
diff --git a/src/authentication/db-connection.js b/src/authentication/db-connection.js
index dc74e3f..9131cf4 100644
--- a/src/authentication/db-connection.js
+++ b/src/authentication/db-connection.js
@@ -29,7 +29,7 @@ DBConnection.prototype.signup = function (options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));
@@ -52,7 +52,7 @@ DBConnection.prototype.changePassword = function (options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));
diff --git a/src/authentication/index.js b/src/authentication/index.js
index f1a4be6..a924d24 100644
--- a/src/authentication/index.js
+++ b/src/authentication/index.js
@@ -91,7 +91,49 @@ Authentication.prototype.buildLogoutUrl = function (options) {
   return urljoin(this.baseOptions.rootUrl, 'v2', 'logout', '?' + qString);
 };
 
-Authentication.prototype.ro = function (options, cb) {
+Authentication.prototype.login = function (options, cb) {
+  assert.check(options, { type: 'object', message: 'options parameter is not valid' }, {
+    clientID: { optional: true, type: 'string', message: 'clientID option is required' },
+    username: { optional: true, type: 'string', message: 'username option is required' },
+    password: { optional: true, type: 'string', message: 'password option is required' },
+    scope: { optional: true, type: 'string', message: 'scope option is required' },
+    audience: { optional: true, type: 'string', message: 'audience option is required' }
+  });
+  assert.check(cb, { type: 'function', message: 'cb parameter is not valid' });
+
+  options.grantType = 'password';
+
+  return this.oauthToken(options, cb);
+};
+
+Authentication.prototype.oauthToken = function (options, cb) {
+  var url;
+  var body;
+
+  assert.check(options, { type: 'object', message: 'options parameter is not valid' }, {
+    grantType: { optional: true, type: 'string', message: 'grantType option is required' }
+  });
+  assert.check(cb, { type: 'function', message: 'cb parameter is not valid' });
+
+  url = urljoin(this.baseOptions.rootUrl, 'oauth', 'token');
+
+  body = objectHelper.merge(this.baseOptions, [
+    'clientID',
+    'scope',
+    'audience'
+  ]).with(options);
+
+  body = objectHelper.toSnakeCase(body, ['auth0Client']);
+
+  body.grant_type = body.grant_type || 'password';
+
+  return this.request
+    .post(url)
+    .send(body)
+    .end(responseHandler(cb));
+};
+
+Authentication.prototype.loginWithResourceOwner = function (options, cb) {
   var url;
   var body;
 
@@ -106,14 +148,17 @@ Authentication.prototype.ro = function (options, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'oauth', 'ro');
 
-  body = objectHelper.merge(this.baseOptions, ['clientID'])
-                .with(options);
+  body = objectHelper.merge(this.baseOptions, [
+    'clientID',
+    'scope',
+    'audience'
+  ]).with(options);
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
   body.grant_type = body.grant_type || 'password';
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));
@@ -127,7 +172,7 @@ Authentication.prototype.userInfo = function (accessToken, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'userinfo');
 
-  this.request
+  return this.request
     .get(url)
     .set('Authorization', 'Bearer ' + accessToken)
     .end(responseHandler(cb));
@@ -149,7 +194,7 @@ Authentication.prototype.delegation = function (options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));
diff --git a/src/authentication/passwordless-authentication.js b/src/authentication/passwordless-authentication.js
index 2bff61c..3bce421 100644
--- a/src/authentication/passwordless-authentication.js
+++ b/src/authentication/passwordless-authentication.js
@@ -83,7 +83,7 @@ PasswordlessAuthentication.prototype.start = function (options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));
@@ -114,7 +114,7 @@ PasswordlessAuthentication.prototype.verify = function (options, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'passwordless', 'verify');
 
-  this.request
+  return this.request
     .post(url)
     .send(cleanOption)
     .end(responseHandler(cb));
diff --git a/src/helper/request-builder.js b/src/helper/request-builder.js
index 67560b8..6166235 100644
--- a/src/helper/request-builder.js
+++ b/src/helper/request-builder.js
@@ -3,6 +3,59 @@ var request = require('superagent');
 var base64Url = require('./base64_url');
 var version = require('../version');
 
+// ------------------------------------------------ RequestWrapper
+
+function RequestWrapper(req) {
+  this.request = req;
+  this.method = req.method;
+  this.url = req.url;
+  this.body = req._data;
+  this.headers = req._header;
+}
+
+RequestWrapper.prototype.abort = function () {
+  this.request.abort();
+};
+
+RequestWrapper.prototype.getMethod = function () {
+  return this.method;
+};
+
+RequestWrapper.prototype.getBody = function () {
+  return this.body;
+};
+
+RequestWrapper.prototype.getUrl = function () {
+  return this.url;
+};
+
+RequestWrapper.prototype.getHeaders = function () {
+  return this.headers;
+};
+
+// ------------------------------------------------ RequestObj
+
+function RequestObj(req) {
+  this.request = req;
+}
+
+RequestObj.prototype.set = function (key, value) {
+  this.request = this.request.set(key, value);
+  return this;
+};
+
+RequestObj.prototype.send = function (body) {
+  this.request = this.request.send(body);
+  return this;
+};
+
+RequestObj.prototype.end = function (cb) {
+  this.request = this.request.end(cb);
+  return new RequestWrapper(this.request);
+};
+
+// ------------------------------------------------ RequestBuilder
+
 function RequestBuilder(options) {
   this._sendTelemetry = options._sendTelemetry === false ? options._sendTelemetry : true;
   this._telemetryInfo = options._telemetryInfo || null;
@@ -28,15 +81,15 @@ RequestBuilder.prototype.getTelemetryData = function () {
 };
 
 RequestBuilder.prototype.get = function (url) {
-  return this.setCommonConfiguration(request.get(url));
+  return new RequestObj(this.setCommonConfiguration(request.get(url)));
 };
 
 RequestBuilder.prototype.post = function (url) {
-  return this.setCommonConfiguration(request.post(url));
+  return new RequestObj(this.setCommonConfiguration(request.post(url)));
 };
 
 RequestBuilder.prototype.patch = function (url) {
-  return this.setCommonConfiguration(request.patch(url));
+  return new RequestObj(this.setCommonConfiguration(request.patch(url)));
 };
 
 module.exports = RequestBuilder;
diff --git a/src/helper/response-handler.js b/src/helper/response-handler.js
index 02587b1..5352c69 100644
--- a/src/helper/response-handler.js
+++ b/src/helper/response-handler.js
@@ -1,7 +1,28 @@
 function wrapCallback(cb) {
   return function (err, data) {
+
     if (err) {
-      return cb(err);
+      var data = {
+        original: err
+      };
+
+      if (err.response && err.response.statusCode) {
+        data.status_code = err.response.statusCode;
+      }
+
+      if (err.response && err.response.statusText) {
+        data.status_text = err.response.statusText;
+      }
+
+      if (err.response && err.response.body) {
+        err = err.response.body;
+      }
+
+      data.code = err.error || err.code || err.error_code || null;
+      data.description = err.error_description || err.description || err.error || null;
+      data.name = err.name || null;
+
+      return cb(data);
     }
 
     return cb(null, data.body || data.text);
diff --git a/src/management/index.js b/src/management/index.js
index dce1de2..445538c 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -30,7 +30,7 @@ Management.prototype.getUser = function (userId, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'users', userId);
 
-  this.request
+  return this.request
     .get(url)
     .end(responseHandler(cb));
 };
@@ -44,24 +44,24 @@ Management.prototype.patchUserMetadata = function (userId, userMetadata, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'users', userId);
 
-  this.request
+  return this.request
     .patch(url)
     .send({ user_metadata: userMetadata })
     .end(responseHandler(cb));
 };
 
-Management.prototype.linkUsers = function (userId, secondaryUserToken, cb) {
+Management.prototype.linkUser = function (userId, secondaryUserToken, cb) {
   var url;
   /* eslint-disable */
   assert.check(userId, { type: 'string', message: 'userId parameter is not valid' });
-  assert.check(secondaryUserToken, { type: 'string', 
+  assert.check(secondaryUserToken, { type: 'string',
     message: 'secondaryUserToken parameter is not valid' });
   assert.check(cb, { type: 'function', message: 'cb parameter is not valid' });
   /* eslint-enable */
 
   url = urljoin(this.baseOptions.rootUrl, 'users', userId, 'identities');
 
-  this.request
+  return this.request
     .post(url)
     .send({ link_with: secondaryUserToken })
     .end(responseHandler(cb));
diff --git a/src/web-auth/index.js b/src/web-auth/index.js
index e6e21fb..c58579e 100644
--- a/src/web-auth/index.js
+++ b/src/web-auth/index.js
@@ -7,6 +7,7 @@ var objectHelper = require('../helper/object');
 var Authentication = require('../authentication');
 var Redirect = require('./redirect');
 var SilentAuthenticationHandler = require('./silent-authentication-handler');
+var windowHelper = require('../helper/window');
 
 function WebAuth(options) {
   /* eslint-disable */
@@ -30,8 +31,8 @@ function WebAuth(options) {
 
   this.baseOptions.tenant = this.baseOptions.domain.split('.')[0];
 
-  this.authentication = new Authentication(this.baseOptions);
-  this.redirect = new Redirect(this.authentication, this.baseOptions);
+  this.client = new Authentication(this.baseOptions);
+  this.redirect = new Redirect(this.client, this.baseOptions);
 }
 
 WebAuth.prototype.parseHash = function (hash) {
@@ -92,7 +93,7 @@ WebAuth.prototype.parseHash = function (hash) {
 
 WebAuth.prototype.renewAuth = function (options, cb) {
   var handler;
-  var usePostMessage = options.usePostMessage || false;
+  var usePostMessage = !!options.usePostMessage;
 
   var params = objectHelper.merge(this.baseOptions, [
     'clientID',
@@ -114,20 +115,44 @@ WebAuth.prototype.renewAuth = function (options, cb) {
 
   params = objectHelper.toSnakeCase(params, ['auth0Client']);
 
-  handler = new SilentAuthenticationHandler(this, this.authentication.buildAuthorizeUrl(params));
+  handler = new SilentAuthenticationHandler(this, this.client.buildAuthorizeUrl(params));
   handler.login(usePostMessage, cb);
 };
 
 WebAuth.prototype.changePassword = function (options, cb) {
-  this.authentication.dbConnection.changePassword(options, cb);
+  return this.client.dbConnection.changePassword(options, cb);
 };
 
 WebAuth.prototype.passwordlessStart = function (options, cb) {
-  this.authentication.passwordless.start(options, cb);
+  return this.client.passwordless.start(options, cb);
+};
+
+WebAuth.prototype.signup = function (options, cb) {
+  return this.client.dbConnection.signup(options, cb);
+};
+
+WebAuth.prototype.login = function (options) {
+  windowHelper.redirect(this.client.buildAuthorizeUrl(options));
+};
+
+WebAuth.prototype.logout = function (options) {
+  windowHelper.redirect(this.client.buildLogoutUrl(options));
+};
+
+WebAuth.prototype.passwordlessVerify = function (options, cb) {
+  var _this = this;
+  return this.client.passwordless.verify(options, function (err) {
+    if (err) {
+      return cb(err);
+    }
+    windowHelper.redirect(_this.client.passwordless.buildVerifyUrl(options));
+  });
 };
 
+
 // popup.login
+// popup.authorize
 // popup.passwordlessVerify
-// popup.signup
+// popup.signupAndLogin
 
 module.exports = WebAuth;
diff --git a/src/web-auth/redirect.js b/src/web-auth/redirect.js
index 3785f97..bbdae43 100644
--- a/src/web-auth/redirect.js
+++ b/src/web-auth/redirect.js
@@ -1,22 +1,14 @@
 var windowHelper = require('../helper/window');
 var UsernamePassword = require('./username-password');
 
-function Redirect(authentication, options) {
+function Redirect(client, options) {
   this.baseOptions = options;
-  this.authentication = authentication;
+  this.client = client;
 }
 
-Redirect.prototype.authorize = function (options) {
-  windowHelper.redirect(this.authentication.buildAuthorizeUrl(options));
-};
-
-Redirect.prototype.logout = function (options) {
-  windowHelper.redirect(this.authentication.buildLogoutUrl(options));
-};
-
 Redirect.prototype.login = function (options, cb) {
   var usernamePassword = new UsernamePassword(this.baseOptions);
-  usernamePassword.login(options, function (err, data) {
+  return usernamePassword.login(options, function (err, data) {
     if (err) {
       return cb(err);
     }
@@ -24,13 +16,9 @@ Redirect.prototype.login = function (options, cb) {
   });
 };
 
-Redirect.prototype.signup = function (options, cb) {
-  this.authentication.dbConnection.signup(options, cb);
-};
-
 Redirect.prototype.signupAndLogin = function (options, cb) {
   var _this = this;
-  this.authentication.dbConnection.signup(options, function (err) {
+  return this.client.dbConnection.signup(options, function (err) {
     if (err) {
       return cb(err);
     }
@@ -38,14 +26,4 @@ Redirect.prototype.signupAndLogin = function (options, cb) {
   });
 };
 
-Redirect.prototype.passwordlessVerify = function (options, cb) {
-  var _this = this;
-  this.authentication.passwordless.verify(options, function (err) {
-    if (err) {
-      return cb(err);
-    }
-    windowHelper.redirect(_this.authentication.passwordless.buildVerifyUrl(options));
-  });
-};
-
 module.exports = Redirect;
diff --git a/src/web-auth/username-password.js b/src/web-auth/username-password.js
index 71fe577..946f669 100644
--- a/src/web-auth/username-password.js
+++ b/src/web-auth/username-password.js
@@ -31,7 +31,7 @@ UsernamePassword.prototype.login = function (options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client']);
 
-  this.request
+  return this.request
     .post(url)
     .send(body)
     .end(responseHandler(cb));


Test patch:
diff --git a/test/authentication/ro.test.js b/test/authentication/ro.test.js
index 78b015e..829e5cf 100644
--- a/test/authentication/ro.test.js
+++ b/test/authentication/ro.test.js
@@ -53,7 +53,7 @@ describe('auth0.authentication', function () {
         });
       });
 
-      this.auth0.ro({
+      this.auth0.loginWithResourceOwner({
         username: 'the username',
         password: 'the password',
         connection: 'the_connection',
@@ -91,14 +91,22 @@ describe('auth0.authentication', function () {
         });
       });
 
-      this.auth0.ro({
+      this.auth0.loginWithResourceOwner({
         username: 'the username',
         password: 'the password',
         connection: 'the_connection',
         scope: 'openid'
       }, function (err, data) {
         expect(data).to.be(undefined);
-        expect(err).to.eql({ error: 'unauthorized', error_description: 'invalid username' });
+        expect(err).to.eql({
+          original: {
+            error: 'unauthorized',
+            error_description: 'invalid username'
+          },
+          code: 'unauthorized',
+          description: 'invalid username',
+          name: null
+        });
         done();
       });
     });
@@ -131,7 +139,7 @@ describe('auth0.authentication', function () {
         });
       });
 
-      this.auth0.ro({
+      this.auth0.loginWithResourceOwner({
         clientID: '123',
         username: 'the username',
         password: 'the password',
diff --git a/test/management/management.test.js b/test/management/management.test.js
index b15c556..1700d34 100644
--- a/test/management/management.test.js
+++ b/test/management/management.test.js
@@ -203,7 +203,7 @@ describe('auth0.Management', function () {
 
     it('should check that userId is valid', function() {
       expect(() => {
-        this.auth0.linkUsers();
+        this.auth0.linkUser();
       }).to.throwException(function (e) {
         expect(e.message).to.be('userId parameter is not valid');
       });
@@ -211,7 +211,7 @@ describe('auth0.Management', function () {
 
     it('should check that secondaryUserToken is valid', function() {
       expect(() => {
-        this.auth0.linkUsers('...');
+        this.auth0.linkUser('...');
       }).to.throwException(function (e) {
         expect(e.message).to.be('secondaryUserToken parameter is not valid');
       });
@@ -219,7 +219,7 @@ describe('auth0.Management', function () {
 
     it('should check that cb is valid', function() {
       expect(() => {
-        this.auth0.linkUsers('...', '...');
+        this.auth0.linkUser('...', '...');
       }).to.throwException(function (e) {
         expect(e.message).to.be('cb parameter is not valid');
       });
@@ -277,7 +277,7 @@ describe('auth0.Management', function () {
         });
       });
 
-      this.auth0.linkUsers('twitter|191919191919191', 'the_second_token', function(err, user) {
+      this.auth0.linkUser('twitter|191919191919191', 'the_second_token', function(err, user) {
         expect(err).to.be(null);
         expect(user).to.eql([{
             'connection': 'twitter',
diff --git a/test/web-auth/redirect.test.js b/test/web-auth/redirect.test.js
index c1cab8b..c06f82d 100644
--- a/test/web-auth/redirect.test.js
+++ b/test/web-auth/redirect.test.js
@@ -52,7 +52,7 @@ describe('auth0.WebAuth.redirect', function () {
         });
       });
 
-      this.auth0.redirect.signup({
+      this.auth0.signup({
         connection: 'the_connection',
         email: 'me@example.com',
         password: '123456'
@@ -165,8 +165,7 @@ describe('auth0.WebAuth.redirect', function () {
             cb({
               'name': 'ValidationError',
               'code': 'invalid_user_password',
-              'description': 'Wrong email or password.',
-              'statusCode': 400
+              'description': 'Wrong email or password.'
             });
           }
         });
@@ -188,10 +187,14 @@ describe('auth0.WebAuth.redirect', function () {
         scope: 'openid'
       }, function (err) {
         expect(err).to.eql({
+          'original': {
+            'name': 'ValidationError',
+            'code': 'invalid_user_password',
+            'description': 'Wrong email or password.'
+          },
           'name': 'ValidationError',
           'code': 'invalid_user_password',
-          'description': 'Wrong email or password.',
-          'statusCode': 400
+          'description': 'Wrong email or password.'
         });
         done();
       });
@@ -233,10 +236,14 @@ describe('auth0.WebAuth.redirect', function () {
             },
             cb: function (cb) {
               cb({
-                'name': 'ValidationError',
-                'code': 'invalid_user_password',
-                'description': 'Wrong email or password.',
-                'statusCode': 400
+                response: {
+                  body: {
+                    'name': 'ValidationError',
+                    'code': 'invalid_user_password',
+                    'description': 'Wrong email or password.'
+                  },
+                  'statusCode': 400
+                }
               });
             }
           });
@@ -274,10 +281,20 @@ describe('auth0.WebAuth.redirect', function () {
       }, function (err, data) {
         expect(data).to.be(undefined);
         expect(err).to.eql({
+          'original': {
+            'response': {
+              'body': {
+                'name': 'ValidationError',
+                'code': 'invalid_user_password',
+                'description': 'Wrong email or password.'
+              },
+              'statusCode': 400
+            }
+          },
           'name': 'ValidationError',
           'code': 'invalid_user_password',
           'description': 'Wrong email or password.',
-          'statusCode': 400
+          'status_code': 400
         });
         done();
       });
@@ -300,10 +317,13 @@ describe('auth0.WebAuth.redirect', function () {
           },
           cb: function (cb) {
             cb({
-              "name":"BadRequestError",
-              "code":"user_exists",
-              "description":"The user already exists.",
-              "statusCode":400
+              response: {
+                "statusCode":400,
+                body: {
+                  "code":"user_exists",
+                  "description":"The user already exists."
+                }
+              }
             });
           }
         });
@@ -317,10 +337,19 @@ describe('auth0.WebAuth.redirect', function () {
       }, function (err, data) {
         expect(data).to.be(undefined);
         expect(err).to.eql({
-          "name":"BadRequestError",
+          original: {
+            response: {
+              "statusCode":400,
+              body: {
+                "code":"user_exists",
+                "description":"The user already exists."
+              }
+            }
+          },
+          "name":null,
           "code":"user_exists",
           "description":"The user already exists.",
-          "statusCode":400
+          "status_code":400
         });
         done();
       });
@@ -368,7 +397,7 @@ describe('auth0.WebAuth.redirect', function () {
         });
       });
 
-      this.auth0.redirect.passwordlessVerify({
+      this.auth0.passwordlessVerify({
         connection: 'the_connection',
         phoneNumber: '123456',
         type: 'sms',
@@ -418,7 +447,7 @@ describe('auth0.WebAuth.redirect', function () {
         });
       });
 
-      this.auth0.redirect.passwordlessVerify({
+      this.auth0.passwordlessVerify({
         connection: 'the_connection',
         phoneNumber: '123456',
         type: 'sms',
@@ -462,15 +491,20 @@ describe('auth0.WebAuth.redirect', function () {
         });
       });
 
-      this.auth0.redirect.passwordlessVerify({
+      this.auth0.passwordlessVerify({
         connection: 'the_connection',
         phoneNumber: '123456',
         type: 'sms',
         verificationCode: 'abc'
       }, function (err) {
         expect(err).to.eql({
-          error: 'some_error_code',
-          error_description: 'Some error description'
+          original: {
+            error: 'some_error_code',
+            error_description: 'Some error description'
+          },
+          name: null,
+          code: 'some_error_code',
+          description: 'Some error description'
         });
         done();
       });
@@ -490,12 +524,12 @@ describe('auth0.WebAuth.redirect', function () {
     });
 
     it('should redirect to authorize', function () {
-      this.auth0.redirect.authorize({connection: 'facebook'})
+      this.auth0.login({connection: 'facebook'})
       expect(global.window.location).to.be('https://me.auth0.com/authorize?client_id=...&response_type=code&redirect_uri=http%3A%2F%2Fpage.com%2Fcallback&connection=facebook');
     });
 
     it('should redirect to logout', function () {
-      this.auth0.redirect.logout({redirect_to: 'http://example.com/logout'})
+      this.auth0.logout({redirect_to: 'http://example.com/logout'})
       expect(global.window.location).to.be('https://me.auth0.com/v2/logout?client_id=...&redirect_to=http%3A%2F%2Fexample.com%2Flogout');
     });
   });