Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added ClientsManager.rotateClientSecret method #721

Merged
merged 5 commits into from May 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/management/ClientsManager.js
Expand Up @@ -54,6 +54,19 @@ class ClientsManager {
options.tokenProvider
);
this.resource = new RetryRestClient(auth0RestClient, options.retry);

/**
* Provides an abstraction layer for consuming the
* {@link https://auth0.com/docs/api/management/v2#!/Clients/post_rotate_secret Auth0 Clients Rotate a client secret}.
*
* @type {external:RestClient}
*/
const auth0RotateSecretClient = new Auth0RestClient(
`${options.baseUrl}/clients/:client_id/rotate-secret`,
clientOptions,
options.tokenProvider
);
this.rotateSecretResource = new RetryRestClient(auth0RotateSecretClient, options.retry);
}

/**
Expand Down Expand Up @@ -166,6 +179,36 @@ class ClientsManager {
delete(...args) {
return this.resource.delete(...args);
}

/**
* Rotate a client secret
*
* @example
* management.clients.rotateClientSecret({ client_id: CLIENT_ID }, function (err, user) {
* if (err) {
* // Handle error.
* }
*
* // Client secret rotated.
* });
* @param {object} params params object
* @param {string} params.client_id Application client ID.
* @returns {Promise|undefined}
*/
rotateClientSecret(params, cb) {
const query = params || {};

// Require a client ID.
if (!params.client_id) {
throw new ArgumentError('The client_id cannot be null or undefined');
}

if (cb && cb instanceof Function) {
return this.rotateSecretResource.create(query, {}, cb);
}

return this.rotateSecretResource.create(query, {});
}
}

module.exports = ClientsManager;
145 changes: 102 additions & 43 deletions test/management/client.tests.js
Expand Up @@ -6,10 +6,16 @@ const API_URL = 'https://tenant.auth0.com';
const ClientsManager = require(`../../src/management/ClientsManager`);
const { ArgumentError } = require('rest-facade');

/**
* @type {ClientsManager}
*/

let clients;

describe('ClientsManager', () => {
before(function () {
this.token = 'TOKEN';
this.clients = new ClientsManager({
clients = new ClientsManager({
headers: {
authorization: `Bearer ${this.token}`,
},
Expand All @@ -25,8 +31,8 @@ describe('ClientsManager', () => {
const methods = ['getAll', 'get', 'create', 'update', 'delete'];

methods.forEach((method) => {
it(`should have a ${method} method`, function () {
expect(this.clients[method]).to.exist.to.be.an.instanceOf(Function);
it(`should have a ${method} method`, () => {
expect(clients[method]).to.exist.to.be.an.instanceOf(Function);
});
});
});
Expand Down Expand Up @@ -56,34 +62,34 @@ describe('ClientsManager', () => {
this.request = nock(API_URL).get('/clients').reply(200);
});

it('should accept a callback', function (done) {
this.clients.getAll(() => {
it('should accept a callback', (done) => {
clients.getAll(() => {
done();
});
});

it('should return a promise if no callback is given', function (done) {
this.clients.getAll().then(done.bind(null, null)).catch(done.bind(null, null));
it('should return a promise if no callback is given', (done) => {
clients.getAll().then(done.bind(null, null)).catch(done.bind(null, null));
});

it('should pass any errors to the promise catch handler', function (done) {
it('should pass any errors to the promise catch handler', (done) => {
nock.cleanAll();

nock(API_URL).get('/clients').reply(500);

this.clients.getAll().catch((err) => {
clients.getAll().catch((err) => {
expect(err).to.exist;
done();
});
});

it('should pass the body of the response to the "then" handler', function (done) {
it('should pass the body of the response to the "then" handler', (done) => {
nock.cleanAll();

const data = [{ test: true }];
nock(API_URL).get('/clients').reply(200, data);

this.clients.getAll().then((clients) => {
clients.getAll().then((clients) => {
expect(clients).to.be.an.instanceOf(Array);

expect(clients.length).to.equal(data.length);
Expand All @@ -97,7 +103,7 @@ describe('ClientsManager', () => {
it('should perform a GET request to /api/v2/clients', function (done) {
const { request } = this;

this.clients.getAll().then(() => {
clients.getAll().then(() => {
expect(request.isDone()).to.be.true;
done();
});
Expand All @@ -111,13 +117,13 @@ describe('ClientsManager', () => {
.matchHeader('Authorization', `Bearer ${this.token}`)
.reply(200);

this.clients.getAll().then(() => {
clients.getAll().then(() => {
expect(request.isDone()).to.be.true;
done();
});
});

it('should pass the parameters in the query-string', function (done) {
it('should pass the parameters in the query-string', (done) => {
nock.cleanAll();

const request = nock(API_URL)
Expand All @@ -128,7 +134,7 @@ describe('ClientsManager', () => {
})
.reply(200);

this.clients.getAll({ include_fields: true, fields: 'test' }).then(() => {
clients.getAll({ include_fields: true, fields: 'test' }).then(() => {
expect(request.isDone()).to.be.true;
done();
});
Expand All @@ -142,18 +148,18 @@ describe('ClientsManager', () => {
this.request = nock(API_URL).post('/clients').reply(201, data);
});

it('should accept a callback', function (done) {
this.clients.create(data, done.bind(null, null));
it('should accept a callback', (done) => {
clients.create(data, done.bind(null, null));
});

it('should return a promise if no callback is given', function (done) {
this.clients.create(data).then(done.bind(null, null)).catch(done.bind(null, null));
it('should return a promise if no callback is given', (done) => {
clients.create(data).then(done.bind(null, null)).catch(done.bind(null, null));
});

it('should perform a POST request to /api/v2/clients', function (done) {
const { request } = this;

this.clients.create(data).then(() => {
clients.create(data).then(() => {
expect(request.isDone()).to.be.true;

done();
Expand All @@ -168,19 +174,19 @@ describe('ClientsManager', () => {
.matchHeader('Authorization', `Bearer ${this.token}`)
.reply(201, data);

this.clients.create(data).then(() => {
clients.create(data).then(() => {
expect(request.isDone()).to.be.true;

done();
});
});

it('should include the new client data in the request body', function (done) {
it('should include the new client data in the request body', (done) => {
nock.cleanAll();

const request = nock(API_URL).post('/clients', data).reply(201, data);

this.clients.create(data).then(() => {
clients.create(data).then(() => {
expect(request.isDone()).to.be.true;

done();
Expand All @@ -202,20 +208,17 @@ describe('ClientsManager', () => {
it('should accept a callback', function (done) {
const params = { id: this.data.id };

this.clients.get(params, done.bind(null, null));
clients.get(params, done.bind(null, null));
});

it('should return a promise if no callback is given', function (done) {
this.clients
.get({ id: this.data.id })
.then(done.bind(null, null))
.catch(done.bind(null, null));
clients.get({ id: this.data.id }).then(done.bind(null, null)).catch(done.bind(null, null));
});

it('should perform a POST request to /api/v2/clients/5', function (done) {
const { request } = this;

this.clients.get({ client_id: this.data.id }).then(() => {
clients.get({ client_id: this.data.id }).then(() => {
expect(request.isDone()).to.be.true;

done();
Expand All @@ -230,21 +233,18 @@ describe('ClientsManager', () => {
this.request = nock(API_URL).patch(`/clients/${this.data.id}`).reply(200, this.data);
});

it('should accept a callback', function (done) {
this.clients.update({ client_id: 5 }, {}, done.bind(null, null));
it('should accept a callback', (done) => {
clients.update({ client_id: 5 }, {}, done.bind(null, null));
});

it('should return a promise if no callback is given', function (done) {
this.clients
.update({ client_id: 5 }, {})
.then(done.bind(null, null))
.catch(done.bind(null, null));
it('should return a promise if no callback is given', (done) => {
clients.update({ client_id: 5 }, {}).then(done.bind(null, null)).catch(done.bind(null, null));
});

it('should perform a PATCH request to /api/v2/clients/5', function (done) {
const { request } = this;

this.clients.update({ client_id: 5 }, {}).then(() => {
clients.update({ client_id: 5 }, {}).then(() => {
expect(request.isDone()).to.be.true;

done();
Expand All @@ -256,7 +256,7 @@ describe('ClientsManager', () => {

const request = nock(API_URL).patch(`/clients/${this.data.id}`, this.data).reply(200);

this.clients.update({ client_id: 5 }, this.data).then(() => {
clients.update({ client_id: 5 }, this.data).then(() => {
expect(request.isDone()).to.be.true;

done();
Expand All @@ -271,22 +271,81 @@ describe('ClientsManager', () => {
this.request = nock(API_URL).delete(`/clients/${id}`).reply(200);
});

it('should accept a callback', function (done) {
this.clients.delete({ client_id: id }, done.bind(null, null));
it('should accept a callback', (done) => {
clients.delete({ client_id: id }, done.bind(null, null));
});

it('should return a promise when no callback is given', function (done) {
this.clients.delete({ client_id: id }).then(done.bind(null, null));
it('should return a promise when no callback is given', (done) => {
clients.delete({ client_id: id }).then(done.bind(null, null));
});

it(`should perform a DELETE request to /clients/${id}`, function (done) {
const { request } = this;

this.clients.delete({ client_id: id }).then(() => {
clients.delete({ client_id: id }).then(() => {
expect(request.isDone()).to.be.true;

done();
});
});
});

describe('#rotateSecret', () => {
const client_id = 5;

beforeEach(function () {
this.request = nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(200);
});

it('should accept a callback', (done) => {
clients.rotateClientSecret({ client_id }, done.bind(null, null));
});

it('should return a promise if no callback is given', (done) => {
clients
.rotateClientSecret({ client_id }, {})
.then(done.bind(null, null))
.catch(done.bind(null, null));
});

it('should perform a POST request to /api/v2/clients/5/rotate-secret', function (done) {
const { request } = this;

clients.rotateClientSecret({ client_id }).then(() => {
expect(request.isDone()).to.be.true;

done();
});
});

it('should return an error when client_id is not sent', () => {
expect(() => {
clients.rotateClientSecret({});
}).to.throw(ArgumentError, 'The client_id cannot be null or undefined');
});

it('should include the new data in the body of the request', (done) => {
nock.cleanAll();

const request = nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(200);

clients.rotateClientSecret({ client_id }).then(() => {
expect(request.isDone()).to.be.true;

done();
});
});

it('should pass any errors to the promise catch handler', (done) => {
nock.cleanAll();

nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(500);

clients.rotateClientSecret({ client_id }).catch((err) => {
expect(err).to.exist;

done();
});
});
});
});