Skip to content

Commit ff8f2e7

Browse files
padamstxdpopp07
authored andcommitted
feat: allow IAM clientid/secret to be configured (#14)
1 parent f7e65d8 commit ff8f2e7

File tree

3 files changed

+198
-3
lines changed

3 files changed

+198
-3
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ index.js
1717
**/*.js.map
1818
coverage.lcov
1919
.swagger-codegen-ignore
20+
/.settings/
21+
/.project

iam-token-manager/v1.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@
1414
* limitations under the License.
1515
*/
1616

17+
import bufferFrom = require('buffer-from');
1718
import extend = require('extend');
1819
import { sendRequest } from '../lib/requestwrapper';
1920

2021
export type Options = {
2122
iamApikey?: string;
2223
iamAccessToken?: string;
2324
iamUrl?: string;
25+
iamClientId?: string;
26+
iamSecret?: string;
2427
}
2528

2629
// this interface is a representation of the response
@@ -41,6 +44,8 @@ export class IamTokenManagerV1 {
4144
protected tokenInfo: IamTokenData;
4245
private iamApikey: string;
4346
private userAccessToken: string;
47+
private iamClientId: string;
48+
private iamSecret: string;
4449

4550
/**
4651
* IAM Token Manager Service
@@ -54,14 +59,20 @@ export class IamTokenManagerV1 {
5459
* @constructor
5560
*/
5661
constructor(options: Options) {
57-
this.iamUrl = options.iamUrl || 'https://iam.bluemix.net/identity/token';
62+
this.iamUrl = options.iamUrl || 'https://iam.cloud.ibm.com/identity/token';
5863
this.tokenInfo = {} as IamTokenData;
5964
if (options.iamApikey) {
6065
this.iamApikey = options.iamApikey;
6166
}
6267
if (options.iamAccessToken) {
6368
this.userAccessToken = options.iamAccessToken;
6469
}
70+
if (options.iamClientId) {
71+
this.iamClientId = options.iamClientId;
72+
}
73+
if (options.iamSecret) {
74+
this.iamSecret = options.iamSecret;
75+
}
6576
}
6677

6778
/**
@@ -96,6 +107,22 @@ export class IamTokenManagerV1 {
96107
}
97108
}
98109

110+
/**
111+
* Set the IAM 'client_id' and 'secret' values.
112+
* These values are used to compute the Authorization header used
113+
* when retrieving or refreshing the IAM access token.
114+
* If these values are not set, then a default Authorization header
115+
* will be used when interacting with the IAM token server.
116+
*
117+
* @param {string} iamClientId - The client id
118+
* @param {string} iamSecret - The secret
119+
* @returns {void}
120+
*/
121+
public setIamAuthorizationInfo(iamClientId: string, iamSecret: string): void {
122+
this.iamClientId = iamClientId;
123+
this.iamSecret = iamSecret;
124+
}
125+
99126
/**
100127
* Set a self-managed IAM access token.
101128
* The access token should be valid and not yet expired.
@@ -125,7 +152,7 @@ export class IamTokenManagerV1 {
125152
method: 'POST',
126153
headers: {
127154
'Content-type': 'application/x-www-form-urlencoded',
128-
Authorization: 'Basic Yng6Yng='
155+
Authorization: this.computeIamAuthHeader()
129156
},
130157
form: {
131158
grant_type: 'urn:ibm:params:oauth:grant-type:apikey',
@@ -150,7 +177,7 @@ export class IamTokenManagerV1 {
150177
method: 'POST',
151178
headers: {
152179
'Content-type': 'application/x-www-form-urlencoded',
153-
Authorization: 'Basic Yng6Yng='
180+
Authorization: this.computeIamAuthHeader()
154181
},
155182
form: {
156183
grant_type: 'refresh_token',
@@ -213,4 +240,22 @@ export class IamTokenManagerV1 {
213240
private saveTokenInfo(tokenResponse: IamTokenData): void {
214241
this.tokenInfo = extend({}, tokenResponse);
215242
}
243+
244+
/**
245+
* Compute and return the Authorization header to be used with the
246+
* IAM token server interactions (retrieve and refresh access token).
247+
*/
248+
private computeIamAuthHeader(): string {
249+
// Use bx:bx as default auth header creds.
250+
let clientId = 'bx';
251+
let secret = 'bx';
252+
253+
// If both the clientId and secret were specified by the user, then use them.
254+
if (this.iamClientId && this.iamSecret) {
255+
clientId = this.iamClientId;
256+
secret = this.iamSecret;
257+
}
258+
const encodedCreds = bufferFrom(`${clientId}:${secret}`).toString('base64');
259+
return `Basic ${encodedCreds}`;
260+
}
216261
}

test/unit/iamTokenManager.test.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,152 @@ describe('iam_token_manager_v1', function() {
199199
done();
200200
});
201201
});
202+
203+
it('should use the default Authorization header - no clientid, no secret', function(done) {
204+
const instance = new IamTokenManagerV1({ iamApikey: 'abcd-1234' });
205+
206+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
207+
_callback();
208+
});
209+
210+
instance.getToken(function() {
211+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
212+
const authHeader = sendRequestArgs.options.headers.Authorization;
213+
expect(authHeader).toBe('Basic Yng6Yng=');
214+
done();
215+
});
216+
});
217+
218+
it('should use a non-default Authorization header - client id and secret via ctor', function(done) {
219+
const instance = new IamTokenManagerV1({
220+
iamApikey: 'abcd-1234',
221+
iamClientId: 'foo',
222+
iamSecret: 'bar',
223+
});
224+
225+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
226+
_callback();
227+
});
228+
229+
instance.getToken(function() {
230+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
231+
const authHeader = sendRequestArgs.options.headers.Authorization;
232+
expect(authHeader).not.toBe('Basic Yng6Yng=');
233+
done();
234+
});
235+
});
236+
237+
it('should use the default Authorization header - clientid only via ctor', function(done) {
238+
const instance = new IamTokenManagerV1({
239+
iamApikey: 'abcd-1234',
240+
iamClientId: 'foo',
241+
});
242+
243+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
244+
_callback();
245+
});
246+
247+
instance.getToken(function() {
248+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
249+
const authHeader = sendRequestArgs.options.headers.Authorization;
250+
expect(authHeader).toBe('Basic Yng6Yng=');
251+
done();
252+
});
253+
});
254+
255+
it('should use the default Authorization header, secret only via ctor', function(done) {
256+
const instance = new IamTokenManagerV1({
257+
iamApikey: 'abcd-1234',
258+
iamSecret: 'bar',
259+
});
260+
261+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
262+
_callback();
263+
});
264+
265+
instance.getToken(function() {
266+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
267+
const authHeader = sendRequestArgs.options.headers.Authorization;
268+
expect(authHeader).toBe('Basic Yng6Yng=');
269+
done();
270+
});
271+
});
272+
273+
it('should use a non-default Authorization header - client id and secret via setter', function(done) {
274+
const instance = new IamTokenManagerV1({
275+
iamApikey: 'abcd-1234',
276+
});
277+
278+
instance.setIamAuthorizationInfo('foo', 'bar');
279+
280+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
281+
_callback();
282+
});
283+
284+
instance.getToken(function() {
285+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
286+
const authHeader = sendRequestArgs.options.headers.Authorization;
287+
expect(authHeader).not.toBe('Basic Yng6Yng=');
288+
done();
289+
});
290+
});
291+
292+
it('should use the default Authorization header - clientid only via setter', function(done) {
293+
const instance = new IamTokenManagerV1({
294+
iamApikey: 'abcd-1234',
295+
});
296+
297+
instance.setIamAuthorizationInfo('foo', null);
298+
299+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
300+
_callback();
301+
});
302+
303+
instance.getToken(function() {
304+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
305+
const authHeader = sendRequestArgs.options.headers.Authorization;
306+
expect(authHeader).toBe('Basic Yng6Yng=');
307+
done();
308+
});
309+
});
310+
311+
it('should use the default Authorization header, secret only via ctor', function(done) {
312+
const instance = new IamTokenManagerV1({
313+
iamApikey: 'abcd-1234',
314+
iamSecret: 'bar',
315+
});
316+
317+
instance.setIamAuthorizationInfo(null, 'bar');
318+
319+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
320+
_callback();
321+
});
322+
323+
instance.getToken(function() {
324+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
325+
const authHeader = sendRequestArgs.options.headers.Authorization;
326+
expect(authHeader).toBe('Basic Yng6Yng=');
327+
done();
328+
});
329+
});
330+
331+
it('should use the default Authorization header, nulls passed to setter', function(done) {
332+
const instance = new IamTokenManagerV1({
333+
iamApikey: 'abcd-1234',
334+
iamSecret: 'bar',
335+
});
336+
337+
instance.setIamAuthorizationInfo(null, null);
338+
339+
requestWrapper.sendRequest.mockImplementation((parameters, _callback) => {
340+
_callback();
341+
});
342+
343+
instance.getToken(function() {
344+
const sendRequestArgs = requestWrapper.sendRequest.mock.calls[0][0];
345+
const authHeader = sendRequestArgs.options.headers.Authorization;
346+
expect(authHeader).toBe('Basic Yng6Yng=');
347+
done();
348+
});
349+
});
202350
});

0 commit comments

Comments
 (0)