123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385 |
- var AWS = require('../core');
- var CognitoIdentity = require('../../clients/cognitoidentity');
- var STS = require('../../clients/sts');
- /**
- * Represents credentials retrieved from STS Web Identity Federation using
- * the Amazon Cognito Identity service.
- *
- * By default this provider gets credentials using the
- * {AWS.CognitoIdentity.getCredentialsForIdentity} service operation, which
- * requires either an `IdentityId` or an `IdentityPoolId` (Amazon Cognito
- * Identity Pool ID), which is used to call {AWS.CognitoIdentity.getId} to
- * obtain an `IdentityId`. If the identity or identity pool is not configured in
- * the Amazon Cognito Console to use IAM roles with the appropriate permissions,
- * then additionally a `RoleArn` is required containing the ARN of the IAM trust
- * policy for the Amazon Cognito role that the user will log into. If a `RoleArn`
- * is provided, then this provider gets credentials using the
- * {AWS.STS.assumeRoleWithWebIdentity} service operation, after first getting an
- * Open ID token from {AWS.CognitoIdentity.getOpenIdToken}.
- *
- * In addition, if this credential provider is used to provide authenticated
- * login, the `Logins` map may be set to the tokens provided by the respective
- * identity providers. See {constructor} for an example on creating a credentials
- * object with proper property values.
- *
- * ## Refreshing Credentials from Identity Service
- *
- * In addition to AWS credentials expiring after a given amount of time, the
- * login token from the identity provider will also expire. Once this token
- * expires, it will not be usable to refresh AWS credentials, and another
- * token will be needed. The SDK does not manage refreshing of the token value,
- * but this can be done through a "refresh token" supported by most identity
- * providers. Consult the documentation for the identity provider for refreshing
- * tokens. Once the refreshed token is acquired, you should make sure to update
- * this new token in the credentials object's {params} property. The following
- * code will update the WebIdentityToken, assuming you have retrieved an updated
- * token from the identity provider:
- *
- * ```javascript
- * AWS.config.credentials.params.Logins['graph.facebook.com'] = updatedToken;
- * ```
- *
- * Future calls to `credentials.refresh()` will now use the new token.
- *
- * @!attribute params
- * @return [map] the map of params passed to
- * {AWS.CognitoIdentity.getId},
- * {AWS.CognitoIdentity.getOpenIdToken}, and
- * {AWS.STS.assumeRoleWithWebIdentity}. To update the token, set the
- * `params.WebIdentityToken` property.
- * @!attribute data
- * @return [map] the raw data response from the call to
- * {AWS.CognitoIdentity.getCredentialsForIdentity}, or
- * {AWS.STS.assumeRoleWithWebIdentity}. Use this if you want to get
- * access to other properties from the response.
- * @!attribute identityId
- * @return [String] the Cognito ID returned by the last call to
- * {AWS.CognitoIdentity.getOpenIdToken}. This ID represents the actual
- * final resolved identity ID from Amazon Cognito.
- */
- AWS.CognitoIdentityCredentials = AWS.util.inherit(AWS.Credentials, {
- /**
- * @api private
- */
- localStorageKey: {
- id: 'aws.cognito.identity-id.',
- providers: 'aws.cognito.identity-providers.'
- },
- /**
- * Creates a new credentials object.
- * @example Creating a new credentials object
- * AWS.config.credentials = new AWS.CognitoIdentityCredentials({
- *
- * // either IdentityPoolId or IdentityId is required
- * // See the IdentityPoolId param for AWS.CognitoIdentity.getID (linked below)
- * // See the IdentityId param for AWS.CognitoIdentity.getCredentialsForIdentity
- * // or AWS.CognitoIdentity.getOpenIdToken (linked below)
- * IdentityPoolId: 'us-east-1:1699ebc0-7900-4099-b910-2df94f52a030',
- * IdentityId: 'us-east-1:128d0a74-c82f-4553-916d-90053e4a8b0f'
- *
- * // optional, only necessary when the identity pool is not configured
- * // to use IAM roles in the Amazon Cognito Console
- * // See the RoleArn param for AWS.STS.assumeRoleWithWebIdentity (linked below)
- * RoleArn: 'arn:aws:iam::1234567890:role/MYAPP-CognitoIdentity',
- *
- * // optional tokens, used for authenticated login
- * // See the Logins param for AWS.CognitoIdentity.getID (linked below)
- * Logins: {
- * 'graph.facebook.com': 'FBTOKEN',
- * 'www.amazon.com': 'AMAZONTOKEN',
- * 'accounts.google.com': 'GOOGLETOKEN',
- * 'api.twitter.com': 'TWITTERTOKEN',
- * 'www.digits.com': 'DIGITSTOKEN'
- * },
- *
- * // optional name, defaults to web-identity
- * // See the RoleSessionName param for AWS.STS.assumeRoleWithWebIdentity (linked below)
- * RoleSessionName: 'web',
- *
- * // optional, only necessary when application runs in a browser
- * // and multiple users are signed in at once, used for caching
- * LoginId: 'example@gmail.com'
- *
- * }, {
- * // optionally provide configuration to apply to the underlying service clients
- * // if configuration is not provided, then configuration will be pulled from AWS.config
- *
- * // region should match the region your identity pool is located in
- * region: 'us-east-1',
- *
- * // specify timeout options
- * httpOptions: {
- * timeout: 100
- * }
- * });
- * @see AWS.CognitoIdentity.getId
- * @see AWS.CognitoIdentity.getCredentialsForIdentity
- * @see AWS.STS.assumeRoleWithWebIdentity
- * @see AWS.CognitoIdentity.getOpenIdToken
- * @see AWS.Config
- * @note If a region is not provided in the global AWS.config, or
- * specified in the `clientConfig` to the CognitoIdentityCredentials
- * constructor, you may encounter a 'Missing credentials in config' error
- * when calling making a service call.
- */
- constructor: function CognitoIdentityCredentials(params, clientConfig) {
- AWS.Credentials.call(this);
- this.expired = true;
- this.params = params;
- this.data = null;
- this._identityId = null;
- this._clientConfig = AWS.util.copy(clientConfig || {});
- this.loadCachedId();
- var self = this;
- Object.defineProperty(this, 'identityId', {
- get: function() {
- self.loadCachedId();
- return self._identityId || self.params.IdentityId;
- },
- set: function(identityId) {
- self._identityId = identityId;
- }
- });
- },
- /**
- * Refreshes credentials using {AWS.CognitoIdentity.getCredentialsForIdentity},
- * or {AWS.STS.assumeRoleWithWebIdentity}.
- *
- * @callback callback function(err)
- * Called when the STS service responds (or fails). When
- * this callback is called with no error, it means that the credentials
- * information has been loaded into the object (as the `accessKeyId`,
- * `secretAccessKey`, and `sessionToken` properties).
- * @param err [Error] if an error occurred, this value will be filled
- * @see AWS.Credentials.get
- */
- refresh: function refresh(callback) {
- this.coalesceRefresh(callback || AWS.util.fn.callback);
- },
- /**
- * @api private
- * @param callback
- */
- load: function load(callback) {
- var self = this;
- self.createClients();
- self.data = null;
- self._identityId = null;
- self.getId(function(err) {
- if (!err) {
- if (!self.params.RoleArn) {
- self.getCredentialsForIdentity(callback);
- } else {
- self.getCredentialsFromSTS(callback);
- }
- } else {
- self.clearIdOnNotAuthorized(err);
- callback(err);
- }
- });
- },
- /**
- * Clears the cached Cognito ID associated with the currently configured
- * identity pool ID. Use this to manually invalidate your cache if
- * the identity pool ID was deleted.
- */
- clearCachedId: function clearCache() {
- this._identityId = null;
- delete this.params.IdentityId;
- var poolId = this.params.IdentityPoolId;
- var loginId = this.params.LoginId || '';
- delete this.storage[this.localStorageKey.id + poolId + loginId];
- delete this.storage[this.localStorageKey.providers + poolId + loginId];
- },
- /**
- * @api private
- */
- clearIdOnNotAuthorized: function clearIdOnNotAuthorized(err) {
- var self = this;
- if (err.code == 'NotAuthorizedException') {
- self.clearCachedId();
- }
- },
- /**
- * Retrieves a Cognito ID, loading from cache if it was already retrieved
- * on this device.
- *
- * @callback callback function(err, identityId)
- * @param err [Error, null] an error object if the call failed or null if
- * it succeeded.
- * @param identityId [String, null] if successful, the callback will return
- * the Cognito ID.
- * @note If not loaded explicitly, the Cognito ID is loaded and stored in
- * localStorage in the browser environment of a device.
- * @api private
- */
- getId: function getId(callback) {
- var self = this;
- if (typeof self.params.IdentityId === 'string') {
- return callback(null, self.params.IdentityId);
- }
- self.cognito.getId(function(err, data) {
- if (!err && data.IdentityId) {
- self.params.IdentityId = data.IdentityId;
- callback(null, data.IdentityId);
- } else {
- callback(err);
- }
- });
- },
- /**
- * @api private
- */
- loadCredentials: function loadCredentials(data, credentials) {
- if (!data || !credentials) return;
- credentials.expired = false;
- credentials.accessKeyId = data.Credentials.AccessKeyId;
- credentials.secretAccessKey = data.Credentials.SecretKey;
- credentials.sessionToken = data.Credentials.SessionToken;
- credentials.expireTime = data.Credentials.Expiration;
- },
- /**
- * @api private
- */
- getCredentialsForIdentity: function getCredentialsForIdentity(callback) {
- var self = this;
- self.cognito.getCredentialsForIdentity(function(err, data) {
- if (!err) {
- self.cacheId(data);
- self.data = data;
- self.loadCredentials(self.data, self);
- } else {
- self.clearIdOnNotAuthorized(err);
- }
- callback(err);
- });
- },
- /**
- * @api private
- */
- getCredentialsFromSTS: function getCredentialsFromSTS(callback) {
- var self = this;
- self.cognito.getOpenIdToken(function(err, data) {
- if (!err) {
- self.cacheId(data);
- self.params.WebIdentityToken = data.Token;
- self.webIdentityCredentials.refresh(function(webErr) {
- if (!webErr) {
- self.data = self.webIdentityCredentials.data;
- self.sts.credentialsFrom(self.data, self);
- }
- callback(webErr);
- });
- } else {
- self.clearIdOnNotAuthorized(err);
- callback(err);
- }
- });
- },
- /**
- * @api private
- */
- loadCachedId: function loadCachedId() {
- var self = this;
- // in the browser we source default IdentityId from localStorage
- if (AWS.util.isBrowser() && !self.params.IdentityId) {
- var id = self.getStorage('id');
- if (id && self.params.Logins) {
- var actualProviders = Object.keys(self.params.Logins);
- var cachedProviders =
- (self.getStorage('providers') || '').split(',');
- // only load ID if at least one provider used this ID before
- var intersect = cachedProviders.filter(function(n) {
- return actualProviders.indexOf(n) !== -1;
- });
- if (intersect.length !== 0) {
- self.params.IdentityId = id;
- }
- } else if (id) {
- self.params.IdentityId = id;
- }
- }
- },
- /**
- * @api private
- */
- createClients: function() {
- var clientConfig = this._clientConfig;
- this.webIdentityCredentials = this.webIdentityCredentials ||
- new AWS.WebIdentityCredentials(this.params, clientConfig);
- if (!this.cognito) {
- var cognitoConfig = AWS.util.merge({}, clientConfig);
- cognitoConfig.params = this.params;
- this.cognito = new CognitoIdentity(cognitoConfig);
- }
- this.sts = this.sts || new STS(clientConfig);
- },
- /**
- * @api private
- */
- cacheId: function cacheId(data) {
- this._identityId = data.IdentityId;
- this.params.IdentityId = this._identityId;
- // cache this IdentityId in browser localStorage if possible
- if (AWS.util.isBrowser()) {
- this.setStorage('id', data.IdentityId);
- if (this.params.Logins) {
- this.setStorage('providers', Object.keys(this.params.Logins).join(','));
- }
- }
- },
- /**
- * @api private
- */
- getStorage: function getStorage(key) {
- return this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')];
- },
- /**
- * @api private
- */
- setStorage: function setStorage(key, val) {
- try {
- this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')] = val;
- } catch (_) {}
- },
- /**
- * @api private
- */
- storage: (function() {
- try {
- var storage = AWS.util.isBrowser() && window.localStorage !== null && typeof window.localStorage === 'object' ?
- window.localStorage : {};
- // Test set/remove which would throw an error in Safari's private browsing
- storage['aws.test-storage'] = 'foobar';
- delete storage['aws.test-storage'];
- return storage;
- } catch (_) {
- return {};
- }
- })()
- });
|