Skip to content

Commit aa60490

Browse files
Add token based authentication (#4192)
Co-authored-by: AllanZhengYP <[email protected]>
1 parent 50e1c42 commit aa60490

22 files changed

+1115
-76
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "Token",
4+
"description": "Read sso data from sso-session section in SSOTokenProvider"
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "Token",
4+
"description": "Set token from AWS.Signers.Bearer when specified by authtype/signatureVersion"
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"type": "feature",
3+
"category": "token",
4+
"description": "Add TokenProviderChain with Token/Static/SSO"
5+
}

lib/config-base.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,19 @@ import {Agent as httpsAgent} from 'https';
33
import {AWSError} from './error';
44
import {Credentials, CredentialsOptions} from './credentials';
55
import {CredentialProviderChain} from './credentials/credential_provider_chain';
6+
import {Token} from './token';
7+
import {TokenProviderChain} from './token/token_provider_chain';
68

79
export class ConfigBase extends ConfigurationOptions{
810
constructor(options?: ConfigurationOptions);
911
/**
1012
* Loads credentials from the configuration object.
1113
*/
1214
getCredentials(callback: (err: AWSError|null, credentials: Credentials|CredentialsOptions|null) => void): void;
15+
/**
16+
* Loads token from the token object.
17+
*/
18+
getToken(callback: (err: AWSError|null, token: Token|null) => void): void;
1319
/**
1420
* Loads configuration data from a JSON file into this config object.
1521
* Loading configuration will reset all existing configuration on the object.
@@ -149,6 +155,14 @@ export abstract class ConfigurationOptions {
149155
* The provider chain used to resolve credentials if no static credentials property is set.
150156
*/
151157
credentialProvider?: CredentialProviderChain
158+
/**
159+
* The Token to authenticate requests with.
160+
*/
161+
token?: Token|null
162+
/**
163+
* The provider chain used to resolve token if no static token property is set.
164+
*/
165+
tokenProvider?: TokenProviderChain
152166
/**
153167
* AWS access key ID.
154168
*

lib/config.js

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,82 @@ AWS.Config = AWS.util.inherit({
442442
}
443443
},
444444

445+
/**
446+
* Loads token from the configuration object. This is used internally
447+
* by the SDK to ensure that refreshable {Token} objects are properly
448+
* refreshed and loaded when sending a request. If you want to ensure that
449+
* your token is loaded prior to a request, you can use this method
450+
* directly to provide accurate token data stored in the object.
451+
*
452+
* @note If you configure the SDK with static token, the token data should
453+
* already be present in {token} attribute. This method is primarily necessary
454+
* to load token from asynchronous sources, or sources that can refresh
455+
* token periodically.
456+
* @example Getting your access token
457+
* AWS.config.getToken(function(err) {
458+
* if (err) console.log(err.stack); // token not loaded
459+
* else console.log("Token:", AWS.config.token.token);
460+
* })
461+
* @callback callback function(err)
462+
* Called when the {token} have been properly set on the configuration object.
463+
*
464+
* @param err [Error] if this is set, token was not successfully loaded and
465+
* this error provides information why.
466+
* @see token
467+
*/
468+
getToken: function getToken(callback) {
469+
var self = this;
470+
471+
function finish(err) {
472+
callback(err, err ? null : self.token);
473+
}
474+
475+
function tokenError(msg, err) {
476+
return new AWS.util.error(err || new Error(), {
477+
code: 'TokenError',
478+
message: msg,
479+
name: 'TokenError'
480+
});
481+
}
482+
483+
function getAsyncToken() {
484+
self.token.get(function(err) {
485+
if (err) {
486+
var msg = 'Could not load token from ' +
487+
self.token.constructor.name;
488+
err = tokenError(msg, err);
489+
}
490+
finish(err);
491+
});
492+
}
493+
494+
function getStaticToken() {
495+
var err = null;
496+
if (!self.token.token) {
497+
err = tokenError('Missing token');
498+
}
499+
finish(err);
500+
}
501+
502+
if (self.token) {
503+
if (typeof self.token.get === 'function') {
504+
getAsyncToken();
505+
} else { // static token
506+
getStaticToken();
507+
}
508+
} else if (self.tokenProvider) {
509+
self.tokenProvider.resolve(function(err, token) {
510+
if (err) {
511+
err = tokenError('Could not load token from any providers', err);
512+
}
513+
self.token = token;
514+
finish(err);
515+
});
516+
} else {
517+
finish(tokenError('No token to load'));
518+
}
519+
},
520+
445521
/**
446522
* @!group Loading and Setting Configuration Options
447523
*/
@@ -575,7 +651,8 @@ AWS.Config = AWS.util.inherit({
575651
hostPrefixEnabled: true,
576652
stsRegionalEndpoints: 'legacy',
577653
useFipsEndpoint: false,
578-
useDualstackEndpoint: false
654+
useDualstackEndpoint: false,
655+
token: null
579656
},
580657

581658
/**

lib/event_listeners.js

Lines changed: 49 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -255,37 +255,56 @@ AWS.EventListeners = {
255255
var authtype = operation ? operation.authtype : '';
256256
if (!service.api.signatureVersion && !authtype && !service.config.signatureVersion) return done(); // none
257257

258-
service.config.getCredentials(function (err, credentials) {
259-
if (err) {
260-
req.response.error = err;
261-
return done();
262-
}
258+
if (authtype === 'bearer' || service.config.signatureVersion === 'bearer') {
259+
service.config.getToken(function (err, token) {
260+
if (err) {
261+
req.response.error = err;
262+
return done();
263+
}
263264

264-
try {
265-
var date = service.getSkewCorrectedDate();
266-
var SignerClass = service.getSignerClass(req);
267-
var signer = new SignerClass(req.httpRequest,
268-
service.getSigningName(req),
269-
{
270-
signatureCache: service.config.signatureCache,
271-
operation: operation,
272-
signatureVersion: service.api.signatureVersion
273-
});
274-
signer.setServiceClientId(service._clientId);
275-
276-
// clear old authorization headers
277-
delete req.httpRequest.headers['Authorization'];
278-
delete req.httpRequest.headers['Date'];
279-
delete req.httpRequest.headers['X-Amz-Date'];
280-
281-
// add new authorization
282-
signer.addAuthorization(credentials, date);
283-
req.signedAt = date;
284-
} catch (e) {
285-
req.response.error = e;
286-
}
287-
done();
288-
});
265+
try {
266+
var SignerClass = service.getSignerClass(req);
267+
var signer = new SignerClass(req.httpRequest);
268+
signer.addAuthorization(token);
269+
} catch (e) {
270+
req.response.error = e;
271+
}
272+
done();
273+
});
274+
} else {
275+
service.config.getCredentials(function (err, credentials) {
276+
if (err) {
277+
req.response.error = err;
278+
return done();
279+
}
280+
281+
try {
282+
var date = service.getSkewCorrectedDate();
283+
var SignerClass = service.getSignerClass(req);
284+
var signer = new SignerClass(req.httpRequest,
285+
service.getSigningName(req),
286+
{
287+
signatureCache: service.config.signatureCache,
288+
operation: operation,
289+
signatureVersion: service.api.signatureVersion
290+
});
291+
signer.setServiceClientId(service._clientId);
292+
293+
// clear old authorization headers
294+
delete req.httpRequest.headers['Authorization'];
295+
delete req.httpRequest.headers['Date'];
296+
delete req.httpRequest.headers['X-Amz-Date'];
297+
298+
// add new authorization
299+
signer.addAuthorization(credentials, date);
300+
req.signedAt = date;
301+
} catch (e) {
302+
req.response.error = e;
303+
}
304+
done();
305+
});
306+
307+
}
289308
});
290309

291310
add('VALIDATE_RESPONSE', 'validateResponse', function VALIDATE_RESPONSE(resp) {

lib/node_loader.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ require('./credentials/shared_ini_file_credentials');
8787
require('./credentials/process_credentials');
8888
require('./credentials/sso_credentials');
8989

90-
// Setup default chain providers
90+
// Setup default providers for credentials chain
9191
// If this changes, please update documentation for
9292
// AWS.CredentialProviderChain.defaultProviders in
9393
// credentials/credential_provider_chain.js
@@ -102,6 +102,19 @@ AWS.CredentialProviderChain.defaultProviders = [
102102
function () { return new AWS.EC2MetadataCredentials(); }
103103
];
104104

105+
// Load custom token providers
106+
require('./token');
107+
require('./token/token_provider_chain');
108+
require('./token/sso_token_provider');
109+
110+
// Setup default providers for token chain
111+
// If this changes, please update documentation for
112+
// AWS.TokenProviderChain.defaultProviders in
113+
// token/token_provider_chain.js
114+
AWS.TokenProviderChain.defaultProviders = [
115+
function () { return new AWS.SSOTokenProvider(); },
116+
];
117+
105118
var getRegion = function() {
106119
var env = process.env;
107120
var region = env.AWS_REGION || env.AMAZON_REGION;
@@ -173,6 +186,9 @@ AWS.util.update(AWS.Config.prototype.keys, {
173186
var region = getRegion();
174187
return region ? getRealRegion(region): undefined;
175188
},
189+
tokenProvider: function() {
190+
return new AWS.TokenProviderChain();
191+
},
176192
useFipsEndpoint: function() {
177193
var region = getRegion();
178194
return isFipsRegion(region)

lib/service.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,8 @@ AWS.Service = inherit({
499499
version = this.config.signatureVersion;
500500
} else if (authtype === 'v4' || authtype === 'v4-unsigned-body') {
501501
version = 'v4';
502+
} else if (authtype === 'bearer') {
503+
version = 'bearer';
502504
} else {
503505
version = this.api.signatureVersion;
504506
}

lib/shared-ini/ini-loader.d.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,4 @@ export class IniLoader{
2727
* @returns {object} object of all profile information in the file
2828
*/
2929
loadFrom(options: LoadFileOptions): IniFileContent;
30-
}
31-
32-
/**
33-
* Read specified file and return parsed config object. This method will always
34-
* read from disk and won't update cache. This is a lower level function of
35-
* loadFrom().
36-
* @param filename [string] valid readable file path containing aws credentials
37-
* or aws configs
38-
* @param isConfig [boolean] true if specified file is an aws config file; false
39-
* if the file is an aws credentials file
40-
*/
41-
export function parseFile(filename: string, isConfig: boolean): IniFileContent;
30+
}

0 commit comments

Comments
 (0)