Skip to content

Commit 0ed8919

Browse files
zen-crabStephen Melvin
andauthored
Added ClientsManager.rotateClientSecret method (#721)
* Added 'Rotate a client secret' endpoint * Removed client_id check * Added client_id check. Added unit test for client_id check. * Fixed eslint errors * Removed reference to this.data Co-authored-by: Stephen Melvin <[email protected]>
1 parent 022286a commit 0ed8919

File tree

2 files changed

+145
-43
lines changed

2 files changed

+145
-43
lines changed

src/management/ClientsManager.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ class ClientsManager {
5454
options.tokenProvider
5555
);
5656
this.resource = new RetryRestClient(auth0RestClient, options.retry);
57+
58+
/**
59+
* Provides an abstraction layer for consuming the
60+
* {@link https://auth0.com/docs/api/management/v2#!/Clients/post_rotate_secret Auth0 Clients Rotate a client secret}.
61+
*
62+
* @type {external:RestClient}
63+
*/
64+
const auth0RotateSecretClient = new Auth0RestClient(
65+
`${options.baseUrl}/clients/:client_id/rotate-secret`,
66+
clientOptions,
67+
options.tokenProvider
68+
);
69+
this.rotateSecretResource = new RetryRestClient(auth0RotateSecretClient, options.retry);
5770
}
5871

5972
/**
@@ -166,6 +179,36 @@ class ClientsManager {
166179
delete(...args) {
167180
return this.resource.delete(...args);
168181
}
182+
183+
/**
184+
* Rotate a client secret
185+
*
186+
* @example
187+
* management.clients.rotateClientSecret({ client_id: CLIENT_ID }, function (err, user) {
188+
* if (err) {
189+
* // Handle error.
190+
* }
191+
*
192+
* // Client secret rotated.
193+
* });
194+
* @param {object} params params object
195+
* @param {string} params.client_id Application client ID.
196+
* @returns {Promise|undefined}
197+
*/
198+
rotateClientSecret(params, cb) {
199+
const query = params || {};
200+
201+
// Require a client ID.
202+
if (!params.client_id) {
203+
throw new ArgumentError('The client_id cannot be null or undefined');
204+
}
205+
206+
if (cb && cb instanceof Function) {
207+
return this.rotateSecretResource.create(query, {}, cb);
208+
}
209+
210+
return this.rotateSecretResource.create(query, {});
211+
}
169212
}
170213

171214
module.exports = ClientsManager;

test/management/client.tests.js

Lines changed: 102 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ const API_URL = 'https://tenant.auth0.com';
66
const ClientsManager = require(`../../src/management/ClientsManager`);
77
const { ArgumentError } = require('rest-facade');
88

9+
/**
10+
* @type {ClientsManager}
11+
*/
12+
13+
let clients;
14+
915
describe('ClientsManager', () => {
1016
before(function () {
1117
this.token = 'TOKEN';
12-
this.clients = new ClientsManager({
18+
clients = new ClientsManager({
1319
headers: {
1420
authorization: `Bearer ${this.token}`,
1521
},
@@ -25,8 +31,8 @@ describe('ClientsManager', () => {
2531
const methods = ['getAll', 'get', 'create', 'update', 'delete'];
2632

2733
methods.forEach((method) => {
28-
it(`should have a ${method} method`, function () {
29-
expect(this.clients[method]).to.exist.to.be.an.instanceOf(Function);
34+
it(`should have a ${method} method`, () => {
35+
expect(clients[method]).to.exist.to.be.an.instanceOf(Function);
3036
});
3137
});
3238
});
@@ -56,34 +62,34 @@ describe('ClientsManager', () => {
5662
this.request = nock(API_URL).get('/clients').reply(200);
5763
});
5864

59-
it('should accept a callback', function (done) {
60-
this.clients.getAll(() => {
65+
it('should accept a callback', (done) => {
66+
clients.getAll(() => {
6167
done();
6268
});
6369
});
6470

65-
it('should return a promise if no callback is given', function (done) {
66-
this.clients.getAll().then(done.bind(null, null)).catch(done.bind(null, null));
71+
it('should return a promise if no callback is given', (done) => {
72+
clients.getAll().then(done.bind(null, null)).catch(done.bind(null, null));
6773
});
6874

69-
it('should pass any errors to the promise catch handler', function (done) {
75+
it('should pass any errors to the promise catch handler', (done) => {
7076
nock.cleanAll();
7177

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

74-
this.clients.getAll().catch((err) => {
80+
clients.getAll().catch((err) => {
7581
expect(err).to.exist;
7682
done();
7783
});
7884
});
7985

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

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

86-
this.clients.getAll().then((clients) => {
92+
clients.getAll().then((clients) => {
8793
expect(clients).to.be.an.instanceOf(Array);
8894

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

100-
this.clients.getAll().then(() => {
106+
clients.getAll().then(() => {
101107
expect(request.isDone()).to.be.true;
102108
done();
103109
});
@@ -111,13 +117,13 @@ describe('ClientsManager', () => {
111117
.matchHeader('Authorization', `Bearer ${this.token}`)
112118
.reply(200);
113119

114-
this.clients.getAll().then(() => {
120+
clients.getAll().then(() => {
115121
expect(request.isDone()).to.be.true;
116122
done();
117123
});
118124
});
119125

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

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

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

145-
it('should accept a callback', function (done) {
146-
this.clients.create(data, done.bind(null, null));
151+
it('should accept a callback', (done) => {
152+
clients.create(data, done.bind(null, null));
147153
});
148154

149-
it('should return a promise if no callback is given', function (done) {
150-
this.clients.create(data).then(done.bind(null, null)).catch(done.bind(null, null));
155+
it('should return a promise if no callback is given', (done) => {
156+
clients.create(data).then(done.bind(null, null)).catch(done.bind(null, null));
151157
});
152158

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

156-
this.clients.create(data).then(() => {
162+
clients.create(data).then(() => {
157163
expect(request.isDone()).to.be.true;
158164

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

171-
this.clients.create(data).then(() => {
177+
clients.create(data).then(() => {
172178
expect(request.isDone()).to.be.true;
173179

174180
done();
175181
});
176182
});
177183

178-
it('should include the new client data in the request body', function (done) {
184+
it('should include the new client data in the request body', (done) => {
179185
nock.cleanAll();
180186

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

183-
this.clients.create(data).then(() => {
189+
clients.create(data).then(() => {
184190
expect(request.isDone()).to.be.true;
185191

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

205-
this.clients.get(params, done.bind(null, null));
211+
clients.get(params, done.bind(null, null));
206212
});
207213

208214
it('should return a promise if no callback is given', function (done) {
209-
this.clients
210-
.get({ id: this.data.id })
211-
.then(done.bind(null, null))
212-
.catch(done.bind(null, null));
215+
clients.get({ id: this.data.id }).then(done.bind(null, null)).catch(done.bind(null, null));
213216
});
214217

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

218-
this.clients.get({ client_id: this.data.id }).then(() => {
221+
clients.get({ client_id: this.data.id }).then(() => {
219222
expect(request.isDone()).to.be.true;
220223

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

233-
it('should accept a callback', function (done) {
234-
this.clients.update({ client_id: 5 }, {}, done.bind(null, null));
236+
it('should accept a callback', (done) => {
237+
clients.update({ client_id: 5 }, {}, done.bind(null, null));
235238
});
236239

237-
it('should return a promise if no callback is given', function (done) {
238-
this.clients
239-
.update({ client_id: 5 }, {})
240-
.then(done.bind(null, null))
241-
.catch(done.bind(null, null));
240+
it('should return a promise if no callback is given', (done) => {
241+
clients.update({ client_id: 5 }, {}).then(done.bind(null, null)).catch(done.bind(null, null));
242242
});
243243

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

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

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

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

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

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

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

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

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

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

288288
done();
289289
});
290290
});
291291
});
292+
293+
describe('#rotateSecret', () => {
294+
const client_id = 5;
295+
296+
beforeEach(function () {
297+
this.request = nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(200);
298+
});
299+
300+
it('should accept a callback', (done) => {
301+
clients.rotateClientSecret({ client_id }, done.bind(null, null));
302+
});
303+
304+
it('should return a promise if no callback is given', (done) => {
305+
clients
306+
.rotateClientSecret({ client_id }, {})
307+
.then(done.bind(null, null))
308+
.catch(done.bind(null, null));
309+
});
310+
311+
it('should perform a POST request to /api/v2/clients/5/rotate-secret', function (done) {
312+
const { request } = this;
313+
314+
clients.rotateClientSecret({ client_id }).then(() => {
315+
expect(request.isDone()).to.be.true;
316+
317+
done();
318+
});
319+
});
320+
321+
it('should return an error when client_id is not sent', () => {
322+
expect(() => {
323+
clients.rotateClientSecret({});
324+
}).to.throw(ArgumentError, 'The client_id cannot be null or undefined');
325+
});
326+
327+
it('should include the new data in the body of the request', (done) => {
328+
nock.cleanAll();
329+
330+
const request = nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(200);
331+
332+
clients.rotateClientSecret({ client_id }).then(() => {
333+
expect(request.isDone()).to.be.true;
334+
335+
done();
336+
});
337+
});
338+
339+
it('should pass any errors to the promise catch handler', (done) => {
340+
nock.cleanAll();
341+
342+
nock(API_URL).post(`/clients/${client_id}/rotate-secret`).reply(500);
343+
344+
clients.rotateClientSecret({ client_id }).catch((err) => {
345+
expect(err).to.exist;
346+
347+
done();
348+
});
349+
});
350+
});
292351
});

0 commit comments

Comments
 (0)