Skip to content

Commit 1f0267c

Browse files
Merge pull request #677 from watson-developer-cloud/accept-access-token
accept access_token
2 parents df7fc0c + 033271c commit 1f0267c

File tree

5 files changed

+62
-32
lines changed

5 files changed

+62
-32
lines changed

lib/base_service.ts

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface UserOptions {
3838
use_unauthenticated?: boolean;
3939
headers?: HeaderOptions;
4040
token?: string;
41+
access_token?: string;
4142
}
4243

4344
export interface BaseServiceOptions extends UserOptions {
@@ -52,18 +53,19 @@ export interface Credentials {
5253
password: string;
5354
api_key: string;
5455
url: string;
56+
access_token: string;
5557
}
5658

5759
function hasCredentials(obj: any): boolean {
58-
return obj && ((obj.username && obj.password) || obj.api_key);
60+
return obj && ((obj.username && obj.password) || obj.api_key || obj.access_token);
5961
}
6062

6163
function hasBasicCredentials(obj: any): boolean {
6264
return obj && obj.username && obj.password;
6365
}
6466

65-
function acceptsApiKey(name: string): boolean {
66-
return name === 'visual_recognition';
67+
function hasAccessToken(obj: any): boolean {
68+
return obj && obj.access_token;
6769
}
6870

6971
export class BaseService {
@@ -132,9 +134,30 @@ export class BaseService {
132134
if (this._options.url) {
133135
_credentials.url = this._options.url;
134136
}
137+
if (this._options.access_token) {
138+
_credentials.access_token = this._options.access_token;
139+
}
135140
return _credentials;
136141
}
137142

143+
/**
144+
* Set an IAM access token to use when authenticating with the service.
145+
* The access token should be valid and not yet expired.
146+
*
147+
* By using this method, you accept responsibility for managing the
148+
* access token yourself. You must set a new access token before this
149+
* one expires. Failing to do so will result in authentication errors
150+
* after this token expires.
151+
*
152+
* @param {string} access_token - A valid, non-expired IAM access token
153+
* @returns {void}
154+
*/
155+
public setAccessToken(access_token: string) { // tslint:disable-line variable-name
156+
this._options.access_token = access_token;
157+
this._options.headers = this._options.headers || {};
158+
this._options.headers.Authorization = `Bearer ${access_token}`;
159+
}
160+
138161
/**
139162
* @private
140163
* @param {UserOptions} options
@@ -162,24 +185,12 @@ export class BaseService {
162185
_options
163186
);
164187
if (!_options.use_unauthenticated) {
165-
if (!hasCredentials(_options) && acceptsApiKey(this.name)) {
166-
throw new Error(
167-
`Argument error: api_key or username/password are required for ${this.name
168-
.toUpperCase()
169-
.replace(
170-
/_/g,
171-
' '
172-
)} ${this.serviceVersion.toUpperCase()} unless use_unauthenticated is set`
173-
);
174-
} else if (!hasCredentials(_options)) {
175-
throw new Error(
176-
`Argument error: username and password are required for ${this.name
177-
.toUpperCase()
178-
.replace(
179-
/_/g,
180-
' '
181-
)} ${this.serviceVersion.toUpperCase()} unless use_unauthenticated is set`
182-
);
188+
if (!hasCredentials(_options)) {
189+
const errorMessage = 'Insufficient credentials provided in ' +
190+
'constructor argument. Refer to the documentation for the ' +
191+
'required parameters. Common examples are username/password, ' +
192+
'api_key, and access_token.';
193+
throw new Error(errorMessage);
183194
}
184195
if (hasBasicCredentials(_options)) {
185196
// Calculate and add Authorization header to base options
@@ -188,6 +199,9 @@ export class BaseService {
188199
).toString('base64');
189200
const authHeader = { Authorization: `Basic ${encodedCredentials}` };
190201
_options.headers = extend(authHeader, _options.headers);
202+
} else if (hasAccessToken(_options)) {
203+
const authHeader = { Authorization: `Bearer ${_options.access_token}` };
204+
_options.headers = extend(authHeader, _options.headers);
191205
} else {
192206
_options.qs = extend({ api_key: _options.api_key }, _options.qs);
193207
}
@@ -221,12 +235,14 @@ export class BaseService {
221235
const _password: string = process.env[`${_name}_PASSWORD`] || process.env[`${_nameWithUnderscore}_PASSWORD`];
222236
const _apiKey: string = process.env[`${_name}_API_KEY`] || process.env[`${_nameWithUnderscore}_API_KEY`];
223237
const _url: string = process.env[`${_name}_URL`] || process.env[`${_nameWithUnderscore}_URL`];
238+
const _accessToken: string = process.env[`${_name}_ACCESS_TOKEN`] || process.env[`${_nameWithUnderscore}_ACCESS_TOKEN`];
224239

225240
return {
226241
username: _username,
227242
password: _password,
228243
api_key: _apiKey,
229-
url: _url
244+
url: _url,
245+
access_token: _accessToken
230246
};
231247
}
232248
/**

lib/requestwrapper.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,19 +110,20 @@ export function formatErrorIfExists(cb: Function): request.RequestCallback {
110110
});
111111
body = null;
112112
}
113-
114113
// If we still don't have an error and there was an error...
115114
if (!error && (response.statusCode < 200 || response.statusCode >= 300)) {
116115
// The JSON stringify for the error below is for the Dialog service
117116
// It stringifies "[object Object]" into the correct error (PR #445)
118117
error = new Error(typeof body === 'object' ? JSON.stringify(body) : body);
119118
error.code = response.statusCode;
120-
if (error.code === 401 || error.code === 403) {
121-
error.body = error.message;
122-
error.message = 'Unauthorized: Access is denied due to invalid credentials.';
123-
}
124119
body = null;
125120
}
121+
122+
// ensure a more descriptive error message
123+
if (error && (error.code === 401 || error.code === 403)) {
124+
error.body = error.message;
125+
error.message = 'Unauthorized: Access is denied due to invalid credentials.';
126+
}
126127
if (error && response && response.headers) {
127128
error[globalTransactionId] = response.headers[globalTransactionId];
128129
}

test/unit/test.base_service.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,17 @@ describe('BaseService', function() {
124124
};
125125
assert.deepEqual(actual, expected);
126126
});
127+
128+
it('should set header with access_token parameter', function() {
129+
const token = 'abc-1234';
130+
const instance = new TestService({ access_token: token });
131+
assert.equal(instance._options.headers['Authorization'], `Bearer ${token}`);
132+
});
133+
134+
it('should update header with setAccessToken', function() {
135+
const instance = new TestService({ access_token: 'abc-1234' });
136+
const newToken = 'zyx-9876';
137+
instance.setAccessToken(newToken);
138+
assert.equal(instance._options.headers['Authorization'], `Bearer ${newToken}`);
139+
});
127140
});

test/unit/test.visual_recognition.v3.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ describe('visual_recognition', function() {
105105
});
106106

107107
it('should throw when no/insufficient credentials are provided', () => {
108-
assert.throws(() => new watson.VisualRecognitionV3(), /use_unauthenticated/);
109-
assert.throws(() => new watson.VisualRecognitionV3({}), /use_unauthenticated/);
108+
assert.throws(() => new watson.VisualRecognitionV3(), /Insufficient credentials/);
109+
assert.throws(() => new watson.VisualRecognitionV3({}), /Insufficient credentials/);
110110
assert.throws(
111111
() => new watson.VisualRecognitionV3({ version: '2016-05-20' }),
112-
/use_unauthenticated/
112+
/Insufficient credentials/
113113
);
114114
assert.throws(
115115
() => new watson.VisualRecognitionV3({ username: 'foo' }),
116-
/use_unauthenticated/
116+
/Insufficient credentials/
117117
);
118118
});
119119

test/unit/test.wrapper.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ describe('wrapper', function() {
3939
use_unauthenticated: false,
4040
version: 'v1',
4141
});
42-
}, /use_unauthenticated/);
42+
}, /Insufficient credentials/);
4343
});
4444

4545
it('should check for missing version', function() {

0 commit comments

Comments
 (0)