Repository: auth0/node-auth0
Pull request #501 (2020-06-19 21:08:32)

feat(guardian): support policies, selected-provider, message-types methods [MFA-310]
### Changes

Adding support for 6 Management API endpoints, related to Guardian/MFA features. Here is the list of API endpoints and their related method added to this SDK:

- `GET /api/v2/guardian/policies`
  - `management.guardian.getPolicies()`
  - `management.getGuardianPolicies()` (alias)
- `PUT /api/v2/guardian/policies`
  - `management.guardian.updatePolicies()`
  - `management.updateGuardianPolicies()` (alias)
- `GET /api/v2/guardian/factors/sms/selected-provider`
  - `management.guardian.getPhoneFactorSelectedProvider()`
  - `management.getGuardianPhoneFactorSelectedProvider()` (alias)
- `PUT /api/v2/guardian/factors/sms/selected-provider`
  - `management.guardian.updatePhoneFactorSelectedProvider()`
  - `management.updateGuardianPhoneFactorSelectedProvider()` (alias)
- `GET /api/v2/guardian/factors/phone/message-types`
  - `management.guardian.getPhoneFactorMessageTypes()`
  - `management.getGuardianPhoneFactorMessageTypes()` (alias)
- `PUT /api/v2/guardian/factors/phone/message-types`
  - `management.guardian.updatePhoneFactorMessageTypes()`
  - `management.updateGuardianPhoneFactorMessageTypes()` (alias)

These endpoints are going to be publicly documented in [our API explorer](https://auth0.com/docs/api/management/v2) once we kick off our Voice MFA beta program.

### References

https://auth0team.atlassian.net/browse/MFA-310

### Testing

- Do a manual round of testing by calling the methods.
- Generate the jsdoc (`npm run jsdoc:generate`) and browse the resulting documentation website to ensure the methods are clearly documented.

- [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/management/GuardianManager.js b/src/management/GuardianManager.js
index 8c443f1fa..e5125d3b5 100644
--- a/src/management/GuardianManager.js
+++ b/src/management/GuardianManager.js
@@ -104,6 +104,48 @@ var GuardianManager = function(options) {
     guardianFactorsProvidersAuth0RestClient,
     options.retry
   );
+
+  /**
+   * Provides an abstraction layer for retrieving Guardian policies.
+   *
+   * @type {external:RestClient}
+   */
+  var guardianPoliciesAuth0RestClient = new Auth0RestClient(
+    options.baseUrl + '/guardian/policies',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.policies = new RetryRestClient(guardianPoliciesAuth0RestClient, options.retry);
+
+  /**
+   * Provides an abstraction layer for retrieving Guardian phone factor selected provider.
+   *
+   * @type {external:RestClient}
+   */
+  var guardianFactorsPhoneSelectedProviderAuth0RestClient = new Auth0RestClient(
+    options.baseUrl + '/guardian/factors/sms/selected-provider',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.factorsPhoneSelectedProvider = new RetryRestClient(
+    guardianFactorsPhoneSelectedProviderAuth0RestClient,
+    options.retry
+  );
+
+  /**
+   * Provides an abstraction layer for retrieving Guardian phone factor message types.
+   *
+   * @type {external:RestClient}
+   */
+  var guardianFactorsPhoneMessageTypesAuth0RestClient = new Auth0RestClient(
+    options.baseUrl + '/guardian/factors/phone/message-types',
+    clientOptions,
+    options.tokenProvider
+  );
+  this.factorsPhoneMessageTypes = new RetryRestClient(
+    guardianFactorsPhoneMessageTypesAuth0RestClient,
+    options.retry
+  );
 };
 
 /**
@@ -280,4 +322,136 @@ utils.wrapPropertyMethod(GuardianManager, 'updateFactorTemplates', 'factorsTempl
  */
 utils.wrapPropertyMethod(GuardianManager, 'updateFactor', 'factors.update');
 
+/**
+ * Get enabled Guardian policies
+ *
+ * @method    getPolicies
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.getPolicies(function (err, policies) {
+ *   console.log(policies);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(GuardianManager, 'getPolicies', 'policies.get');
+
+/**
+ * Update enabled Guardian policies
+ *
+ * @method    updatePolicies
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.updatePolicies({}, [
+ *   'all-applications'
+ * ], function (err, policies) {
+ *   console.log(policies);
+ * });
+ *
+ * @param   {Object}    params            Parameters.
+ * @param   {String[]}  data              Policies to enable. Empty array disables all policies.
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(GuardianManager, 'updatePolicies', 'policies.update');
+
+/**
+ * Get the Guardian phone factor's selected provider
+ *
+ * @method    getPhoneFactorSelectedProvider
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.getPhoneFactorSelectedProvider(function (err, selectedProvider) {
+ *   console.log(selectedProvider);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  GuardianManager,
+  'getPhoneFactorSelectedProvider',
+  'factorsPhoneSelectedProvider.get'
+);
+
+/**
+ * Update the Guardian phone factor's selected provider
+ *
+ * @method    updatePhoneFactorSelectedProvider
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.updatePhoneFactorSelectedProvider({}, {
+ *   provider: 'twilio'
+ * }, function (err, factor) {
+ *   console.log(factor);
+ * });
+ *
+ * @param   {Object}    params            Parameters.
+ * @param   {Object}    data              Updated selected provider data.
+ * @param   {String}    data.provider     Name of the selected provider
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  GuardianManager,
+  'updatePhoneFactorSelectedProvider',
+  'factorsPhoneSelectedProvider.update'
+);
+
+/**
+ * Get the Guardian phone factor's message types
+ *
+ * @method    getPhoneFactorMessageTypes
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.getPhoneFactorMessageTypes(function (err, messageTypes) {
+ *   console.log(messageTypes);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  GuardianManager,
+  'getPhoneFactorMessageTypes',
+  'factorsPhoneMessageTypes.get'
+);
+
+/**
+ * Update the Guardian phone factor's message types
+ *
+ * @method    updatePhoneFactorMessageTypes
+ * @memberOf  module:management.GuardianManager.prototype
+ *
+ * @example
+ * management.guardian.updatePhoneFactorMessageTypes({}, {
+ *   message_types: ['sms', 'voice']
+ * }, function (err, factor) {
+ *   console.log(factor);
+ * });
+ *
+ * @param   {Object}    params                Parameters.
+ * @param   {Object}    data                  Updated selected provider data.
+ * @param   {String[]}  data.message_types    Message types (only `"sms"` and `"voice"` are supported).
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  GuardianManager,
+  'updatePhoneFactorMessageTypes',
+  'factorsPhoneMessageTypes.update'
+);
+
 module.exports = GuardianManager;
diff --git a/src/management/index.js b/src/management/index.js
index 2588e7001..c329fe321 100644
--- a/src/management/index.js
+++ b/src/management/index.js
@@ -2696,6 +2696,138 @@ utils.wrapPropertyMethod(
  */
 utils.wrapPropertyMethod(ManagementClient, 'updateGuardianFactor', 'guardian.updateFactor');
 
+/**
+ * Get enabled Guardian policies
+ *
+ * @method    getGuardianPolicies
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.getGuardianPolicies(function (err, policies) {
+ *   console.log(policies);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'getGuardianPolicies', 'guardian.getPolicies');
+
+/**
+ * Update enabled Guardian policies
+ *
+ * @method    updateGuardianPolicies
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.updateGuardianPolicies({}, [
+ *   'all-applications'
+ * ], function (err, policies) {
+ *   console.log(policies);
+ * });
+ *
+ * @param   {Object}    params            Parameters.
+ * @param   {String[]}  data              Policies to enable. Empty array disables all policies.
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(ManagementClient, 'updateGuardianPolicies', 'guardian.updatePolicies');
+
+/**
+ * Get the Guardian phone factor's selected provider
+ *
+ * @method    getGuardianPhoneFactorSelectedProvider
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.getGuardianPhoneFactorSelectedProvider(function (err, selectedProvider) {
+ *   console.log(selectedProvider);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'getGuardianPhoneFactorSelectedProvider',
+  'guardian.getPhoneFactorSelectedProvider'
+);
+
+/**
+ * Update the Guardian phone factor's selected provider
+ *
+ * @method    updateGuardianPhoneFactorSelectedProvider
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.updateGuardianPhoneFactorSelectedProvider({}, {
+ *   provider: 'twilio'
+ * }, function (err, factor) {
+ *   console.log(factor);
+ * });
+ *
+ * @param   {Object}    params            Parameters.
+ * @param   {Object}    data              Updated selected provider data.
+ * @param   {String}    data.provider     Name of the selected provider
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'updateGuardianPhoneFactorSelectedProvider',
+  'guardian.updatePhoneFactorSelectedProvider'
+);
+
+/**
+ * Get the Guardian phone factor's message types
+ *
+ * @method    getGuardianPhoneFactorMessageTypes
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.getGuardianPhoneFactorMessageTypes(function (err, messageTypes) {
+ *   console.log(messageTypes);
+ * });
+ *
+ * @param   {Function}  [cb]              Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'getGuardianPhoneFactorMessageTypes',
+  'guardian.getPhoneFactorMessageTypes'
+);
+
+/**
+ * Update the Guardian phone factor's message types
+ *
+ * @method    updateGuardianPhoneFactorMessageTypes
+ * @memberOf  module:management.ManagementClient.prototype
+ *
+ * @example
+ * management.updateGuardianPhoneFactorMessageTypes({}, {
+ *   message_types: ['sms', 'voice']
+ * }, function (err, factor) {
+ *   console.log(factor);
+ * });
+ *
+ * @param   {Object}    params                Parameters.
+ * @param   {Object}    data                  Updated selected provider data.
+ * @param   {String[]}  data.message_types    Message types (only `"sms"` and `"voice"` are supported).
+ * @param   {Function}  [cb]                  Callback function.
+ *
+ * @return  {Promise|undefined}
+ */
+utils.wrapPropertyMethod(
+  ManagementClient,
+  'updateGuardianPhoneFactorMessageTypes',
+  'guardian.updatePhoneFactorMessageTypes'
+);
+
 /**
  * Get all roles.
  *


Test patch:
diff --git a/test/management/guardian.tests.js b/test/management/guardian.tests.js
index b3fd95731..cb1113be1 100644
--- a/test/management/guardian.tests.js
+++ b/test/management/guardian.tests.js
@@ -25,7 +25,11 @@ describe('GuardianManager', function() {
       'updateFactorProvider',
       'getFactorTemplates',
       'updateFactorTemplates',
-      'updateFactor'
+      'updateFactor',
+      'getPhoneFactorSelectedProvider',
+      'updatePhoneFactorSelectedProvider',
+      'getPhoneFactorMessageTypes',
+      'updatePhoneFactorMessageTypes'
     ];
 
     methods.forEach(function(method) {
@@ -302,7 +306,7 @@ describe('GuardianManager', function() {
         .catch(done.bind(null, null));
     });
 
-    it('should perform a POST request to /api/v2/guardian/factors', function(done) {
+    it('should perform a GET request to /api/v2/guardian/factors', function(done) {
       var request = this.request;
 
       this.guardian.getFactors().then(function() {
@@ -368,7 +372,7 @@ describe('GuardianManager', function() {
         .catch(done.bind(null, null));
     });
 
-    it('should perform a POST request to /api/v2/guardian/factors/sms/twilio', function(done) {
+    it('should perform a GET request to /api/v2/guardian/factors/sms/twilio', function(done) {
       var request = this.request;
 
       this.guardian.getFactorProvider(this.params).then(function() {
@@ -446,7 +450,10 @@ describe('GuardianManager', function() {
       nock.cleanAll();
 
       var request = nock(API_URL)
-        .put('/guardian/factors/' + this.params.name + '/providers/' + this.params.provider)
+        .put(
+          '/guardian/factors/' + this.params.name + '/providers/' + this.params.provider,
+          this.data
+        )
         .reply(200);
 
       this.guardian.updateFactorProvider(this.params, this.data).then(function() {
@@ -512,7 +519,7 @@ describe('GuardianManager', function() {
         .catch(done.bind(null, null));
     });
 
-    it('should perform a POST request to /api/v2/guardian/factors/sms/templates', function(done) {
+    it('should perform a GET request to /api/v2/guardian/factors/sms/templates', function(done) {
       var request = this.request;
 
       this.guardian.getFactorTemplates(this.params).then(function() {
@@ -589,7 +596,7 @@ describe('GuardianManager', function() {
       nock.cleanAll();
 
       var request = nock(API_URL)
-        .put('/guardian/factors/' + this.params.name + '/templates')
+        .put('/guardian/factors/' + this.params.name + '/templates', this.data)
         .reply(200);
 
       this.guardian.updateFactorTemplates(this.params, this.data).then(function() {
@@ -664,7 +671,7 @@ describe('GuardianManager', function() {
       nock.cleanAll();
 
       var request = nock(API_URL)
-        .put('/guardian/factors/' + this.params.name)
+        .put('/guardian/factors/' + this.params.name, this.data)
         .reply(200);
 
       this.guardian.updateFactor(this.params, this.data).then(function() {
@@ -703,4 +710,415 @@ describe('GuardianManager', function() {
       });
     });
   });
+
+  describe('#getPolicies', function() {
+    beforeEach(function() {
+      this.data = ['all-applications'];
+
+      this.request = nock(API_URL)
+        .get('/guardian/policies')
+        .reply(200, this.data);
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.getPolicies(done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .getPolicies()
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/guardian/policies', function(done) {
+      var request = this.request;
+
+      this.guardian.getPolicies().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/policies')
+        .reply(500);
+
+      this.guardian.getPolicies().catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/policies')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.getPolicies().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#updatePolicies', function() {
+    beforeEach(function() {
+      this.params = {};
+      this.data = ['all-applications'];
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.updatePolicies(this.params, this.data, done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .updatePolicies(this.params, this.data)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a PUT request to /api/v2/guardian/policies', function(done) {
+      var request = nock(API_URL)
+        .put('/guardian/policies')
+        .reply(200, this.data);
+
+      this.guardian.updatePolicies(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the new data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/policies', this.data)
+        .reply(200);
+
+      this.guardian.updatePolicies(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/policies')
+        .reply(500);
+
+      this.guardian.updatePolicies(this.params, this.data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/policies')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.updatePolicies(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#getPhoneFactorSelectedProvider', function() {
+    beforeEach(function() {
+      this.data = {
+        provider: 'twilio'
+      };
+
+      this.request = nock(API_URL)
+        .get('/guardian/factors/sms/selected-provider')
+        .reply(200, this.data);
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.getPhoneFactorSelectedProvider(done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .getPhoneFactorSelectedProvider()
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/guardian/factors/sms/selected-provider', function(done) {
+      var request = this.request;
+
+      this.guardian.getPhoneFactorSelectedProvider().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/factors/sms/selected-provider')
+        .reply(500);
+
+      this.guardian.getPhoneFactorSelectedProvider().catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/factors/sms/selected-provider')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.getPhoneFactorSelectedProvider().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#updatePhoneFactorSelectedProvider', function() {
+    beforeEach(function() {
+      this.params = {};
+      this.data = {
+        provider: 'twilio'
+      };
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.updatePhoneFactorSelectedProvider(
+        this.params,
+        this.data,
+        done.bind(null, null)
+      );
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .updatePhoneFactorSelectedProvider(this.params, this.data)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a PUT request to /api/v2/guardian/factors/sms/selected-provider', function(done) {
+      var request = nock(API_URL)
+        .put('/guardian/factors/sms/selected-provider')
+        .reply(200, this.data);
+
+      this.guardian.updatePhoneFactorSelectedProvider(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the new data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/sms/selected-provider', this.data)
+        .reply(200);
+
+      this.guardian.updatePhoneFactorSelectedProvider(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/sms/selected-provider')
+        .reply(500);
+
+      this.guardian.updatePhoneFactorSelectedProvider(this.params, this.data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/sms/selected-provider')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.updatePhoneFactorSelectedProvider(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#getPhoneFactorMessageTypes', function() {
+    beforeEach(function() {
+      this.data = {
+        message_types: ['sms', 'voice']
+      };
+
+      this.request = nock(API_URL)
+        .get('/guardian/factors/phone/message-types')
+        .reply(200, this.data);
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.getPhoneFactorMessageTypes(done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .getPhoneFactorMessageTypes()
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a GET request to /api/v2/guardian/factors/phone/message-types', function(done) {
+      var request = this.request;
+
+      this.guardian.getPhoneFactorMessageTypes().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/factors/phone/message-types')
+        .reply(500);
+
+      this.guardian.getPhoneFactorMessageTypes().catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .get('/guardian/factors/phone/message-types')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.getPhoneFactorMessageTypes().then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
+
+  describe('#updatePhoneFactorMessageTypes', function() {
+    beforeEach(function() {
+      this.params = {};
+      this.data = {
+        message_types: ['sms', 'voice']
+      };
+    });
+
+    it('should accept a callback', function(done) {
+      this.guardian.updatePhoneFactorMessageTypes(this.params, this.data, done.bind(null, null));
+    });
+
+    it('should return a promise if no callback is given', function(done) {
+      this.guardian
+        .updatePhoneFactorMessageTypes(this.params, this.data)
+        .then(done.bind(null, null))
+        .catch(done.bind(null, null));
+    });
+
+    it('should perform a PUT request to /api/v2/guardian/factors/phone/message-types', function(done) {
+      var request = nock(API_URL)
+        .put('/guardian/factors/phone/message-types')
+        .reply(200, this.data);
+
+      this.guardian.updatePhoneFactorMessageTypes(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should include the new data in the body of the request', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/phone/message-types', this.data)
+        .reply(200);
+
+      this.guardian.updatePhoneFactorMessageTypes(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+
+    it('should pass any errors to the promise catch handler', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/phone/message-types')
+        .reply(500);
+
+      this.guardian.updatePhoneFactorMessageTypes(this.params, this.data).catch(function(err) {
+        expect(err).to.exist;
+
+        done();
+      });
+    });
+
+    it('should include the token in the Authorization header', function(done) {
+      nock.cleanAll();
+
+      var request = nock(API_URL)
+        .put('/guardian/factors/phone/message-types')
+        .matchHeader('Authorization', 'Bearer ' + this.token)
+        .reply(200);
+
+      this.guardian.updatePhoneFactorMessageTypes(this.params, this.data).then(function() {
+        expect(request.isDone()).to.be.true;
+
+        done();
+      });
+    });
+  });
 });
diff --git a/test/management/management-client.tests.js b/test/management/management-client.tests.js
index b543799c8..34a108e79 100644
--- a/test/management/management-client.tests.js
+++ b/test/management/management-client.tests.js
@@ -299,6 +299,12 @@ describe('ManagementClient', function() {
         expect(client.guardian.factorsProviders.restClient.restClient.options.headers).to.contain(
           requestHeaders
         );
+        expect(
+          client.guardian.factorsPhoneSelectedProvider.restClient.restClient.options.headers
+        ).to.contain(requestHeaders);
+        expect(
+          client.guardian.factorsPhoneMessageTypes.restClient.restClient.options.headers
+        ).to.contain(requestHeaders);
 
         expect(client.customDomains.resource.restClient.restClient.options.headers).to.contain(
           requestHeaders
@@ -438,6 +444,12 @@ describe('ManagementClient', function() {
         expect(client.guardian.factorsProviders.restClient.restClient.options.headers).to.contain(
           requestHeaders
         );
+        expect(
+          client.guardian.factorsPhoneSelectedProvider.restClient.restClient.options.headers
+        ).to.contain(requestHeaders);
+        expect(
+          client.guardian.factorsPhoneMessageTypes.restClient.restClient.options.headers
+        ).to.contain(requestHeaders);
 
         expect(client.customDomains.resource.restClient.restClient.options.headers).to.contain(
           requestHeaders
@@ -575,6 +587,12 @@ describe('ManagementClient', function() {
         expect(
           client.guardian.factorsProviders.restClient.restClient.options.headers
         ).to.not.have.property('Auth0-Client');
+        expect(
+          client.guardian.factorsPhoneSelectedProvider.restClient.restClient.options.headers
+        ).to.not.have.property('Auth0-Client');
+        expect(
+          client.guardian.factorsPhoneMessageTypes.restClient.restClient.options.headers
+        ).to.not.have.property('Auth0-Client');
 
         expect(
           client.customDomains.resource.restClient.restClient.options.headers
@@ -715,6 +733,12 @@ describe('ManagementClient', function() {
         expect(
           client.guardian.factorsProviders.restClient.restClient.options.headers
         ).to.not.have.property('Auth0-Client');
+        expect(
+          client.guardian.factorsPhoneSelectedProvider.restClient.restClient.options.headers
+        ).to.not.have.property('Auth0-Client');
+        expect(
+          client.guardian.factorsPhoneMessageTypes.restClient.restClient.options.headers
+        ).to.not.have.property('Auth0-Client');
 
         expect(
           client.customDomains.resource.restClient.restClient.options.headers
@@ -866,6 +890,12 @@ describe('ManagementClient', function() {
       'getGuardianFactorTemplates',
       'updateGuardianFactorTemplates',
       'updateGuardianFactor',
+      'getGuardianPolicies',
+      'updateGuardianPolicies',
+      'getGuardianPhoneFactorSelectedProvider',
+      'updateGuardianPhoneFactorSelectedProvider',
+      'getGuardianPhoneFactorMessageTypes',
+      'updateGuardianPhoneFactorMessageTypes',
       'getUserBlocks',
       'unblockUser',
       'getUserBlocksByIdentifier',
@@ -878,7 +908,7 @@ describe('ManagementClient', function() {
       this.client = new ManagementClient(config);
     });
 
-    methods.forEach(function (method) {
+    methods.forEach(function(method) {
       it('should have a ' + method + ' method', function() {
         expect(this.client[method]).to.exist.to.be.an.instanceOf(Function);
       });