cognito_identity_credentials.js (11742B)
1 var AWS = require('../core'); 2 var CognitoIdentity = require('../../clients/cognitoidentity'); 3 var STS = require('../../clients/sts'); 4 5 /** 6 * Represents credentials retrieved from STS Web Identity Federation using 7 * the Amazon Cognito Identity service. 8 * 9 * By default this provider gets credentials using the 10 * {AWS.CognitoIdentity.getCredentialsForIdentity} service operation, which 11 * requires either an `IdentityId` or an `IdentityPoolId` (Amazon Cognito 12 * Identity Pool ID), which is used to call {AWS.CognitoIdentity.getId} to 13 * obtain an `IdentityId`. If the identity or identity pool is not configured in 14 * the Amazon Cognito Console to use IAM roles with the appropriate permissions, 15 * then additionally a `RoleArn` is required containing the ARN of the IAM trust 16 * policy for the Amazon Cognito role that the user will log into. If a `RoleArn` 17 * is provided, then this provider gets credentials using the 18 * {AWS.STS.assumeRoleWithWebIdentity} service operation, after first getting an 19 * Open ID token from {AWS.CognitoIdentity.getOpenIdToken}. 20 * 21 * In addition, if this credential provider is used to provide authenticated 22 * login, the `Logins` map may be set to the tokens provided by the respective 23 * identity providers. See {constructor} for an example on creating a credentials 24 * object with proper property values. 25 * 26 * ## Refreshing Credentials from Identity Service 27 * 28 * In addition to AWS credentials expiring after a given amount of time, the 29 * login token from the identity provider will also expire. Once this token 30 * expires, it will not be usable to refresh AWS credentials, and another 31 * token will be needed. The SDK does not manage refreshing of the token value, 32 * but this can be done through a "refresh token" supported by most identity 33 * providers. Consult the documentation for the identity provider for refreshing 34 * tokens. Once the refreshed token is acquired, you should make sure to update 35 * this new token in the credentials object's {params} property. The following 36 * code will update the WebIdentityToken, assuming you have retrieved an updated 37 * token from the identity provider: 38 * 39 * ```javascript 40 * AWS.config.credentials.params.Logins['graph.facebook.com'] = updatedToken; 41 * ``` 42 * 43 * Future calls to `credentials.refresh()` will now use the new token. 44 * 45 * @!attribute params 46 * @return [map] the map of params passed to 47 * {AWS.CognitoIdentity.getId}, 48 * {AWS.CognitoIdentity.getOpenIdToken}, and 49 * {AWS.STS.assumeRoleWithWebIdentity}. To update the token, set the 50 * `params.WebIdentityToken` property. 51 * @!attribute data 52 * @return [map] the raw data response from the call to 53 * {AWS.CognitoIdentity.getCredentialsForIdentity}, or 54 * {AWS.STS.assumeRoleWithWebIdentity}. Use this if you want to get 55 * access to other properties from the response. 56 * @!attribute identityId 57 * @return [String] the Cognito ID returned by the last call to 58 * {AWS.CognitoIdentity.getOpenIdToken}. This ID represents the actual 59 * final resolved identity ID from Amazon Cognito. 60 */ 61 AWS.CognitoIdentityCredentials = AWS.util.inherit(AWS.Credentials, { 62 /** 63 * @api private 64 */ 65 localStorageKey: { 66 id: 'aws.cognito.identity-id.', 67 providers: 'aws.cognito.identity-providers.' 68 }, 69 70 /** 71 * Creates a new credentials object. 72 * @example Creating a new credentials object 73 * AWS.config.credentials = new AWS.CognitoIdentityCredentials({ 74 * 75 * // either IdentityPoolId or IdentityId is required 76 * // See the IdentityPoolId param for AWS.CognitoIdentity.getID (linked below) 77 * // See the IdentityId param for AWS.CognitoIdentity.getCredentialsForIdentity 78 * // or AWS.CognitoIdentity.getOpenIdToken (linked below) 79 * IdentityPoolId: 'us-east-1:1699ebc0-7900-4099-b910-2df94f52a030', 80 * IdentityId: 'us-east-1:128d0a74-c82f-4553-916d-90053e4a8b0f' 81 * 82 * // optional, only necessary when the identity pool is not configured 83 * // to use IAM roles in the Amazon Cognito Console 84 * // See the RoleArn param for AWS.STS.assumeRoleWithWebIdentity (linked below) 85 * RoleArn: 'arn:aws:iam::1234567890:role/MYAPP-CognitoIdentity', 86 * 87 * // optional tokens, used for authenticated login 88 * // See the Logins param for AWS.CognitoIdentity.getID (linked below) 89 * Logins: { 90 * 'graph.facebook.com': 'FBTOKEN', 91 * 'www.amazon.com': 'AMAZONTOKEN', 92 * 'accounts.google.com': 'GOOGLETOKEN', 93 * 'api.twitter.com': 'TWITTERTOKEN', 94 * 'www.digits.com': 'DIGITSTOKEN' 95 * }, 96 * 97 * // optional name, defaults to web-identity 98 * // See the RoleSessionName param for AWS.STS.assumeRoleWithWebIdentity (linked below) 99 * RoleSessionName: 'web', 100 * 101 * // optional, only necessary when application runs in a browser 102 * // and multiple users are signed in at once, used for caching 103 * LoginId: 'example@gmail.com' 104 * 105 * }); 106 * @see AWS.CognitoIdentity.getId 107 * @see AWS.CognitoIdentity.getCredentialsForIdentity 108 * @see AWS.STS.assumeRoleWithWebIdentity 109 * @see AWS.CognitoIdentity.getOpenIdToken 110 */ 111 constructor: function CognitoIdentityCredentials(params) { 112 AWS.Credentials.call(this); 113 this.expired = true; 114 this.params = params; 115 this.data = null; 116 this.identityId = null; 117 this.loadCachedId(); 118 }, 119 120 /** 121 * Refreshes credentials using {AWS.CognitoIdentity.getCredentialsForIdentity}, 122 * or {AWS.STS.assumeRoleWithWebIdentity}. 123 * 124 * @callback callback function(err) 125 * Called when the STS service responds (or fails). When 126 * this callback is called with no error, it means that the credentials 127 * information has been loaded into the object (as the `accessKeyId`, 128 * `secretAccessKey`, and `sessionToken` properties). 129 * @param err [Error] if an error occurred, this value will be filled 130 * @see get 131 */ 132 refresh: function refresh(callback) { 133 var self = this; 134 self.createClients(); 135 self.data = null; 136 self.identityId = null; 137 self.getId(function(err) { 138 if (!err) { 139 if (!self.params.RoleArn) { 140 self.getCredentialsForIdentity(callback); 141 } else { 142 self.getCredentialsFromSTS(callback); 143 } 144 } else { 145 self.clearIdOnNotAuthorized(err); 146 callback(err); 147 } 148 }); 149 }, 150 151 /** 152 * Clears the cached Cognito ID associated with the currently configured 153 * identity pool ID. Use this to manually invalidate your cache if 154 * the identity pool ID was deleted. 155 */ 156 clearCachedId: function clearCache() { 157 this.identityId = null; 158 delete this.params.IdentityId; 159 160 var poolId = this.params.IdentityPoolId; 161 var loginId = this.params.LoginId || ''; 162 delete this.storage[this.localStorageKey.id + poolId + loginId]; 163 delete this.storage[this.localStorageKey.providers + poolId + loginId]; 164 }, 165 166 /** 167 * @api private 168 */ 169 clearIdOnNotAuthorized: function clearIdOnNotAuthorized(err) { 170 var self = this; 171 if (err.code == 'NotAuthorizedException') { 172 self.clearCachedId(); 173 } 174 }, 175 176 /** 177 * Retrieves a Cognito ID, loading from cache if it was already retrieved 178 * on this device. 179 * 180 * @callback callback function(err, identityId) 181 * @param err [Error, null] an error object if the call failed or null if 182 * it succeeded. 183 * @param identityId [String, null] if successful, the callback will return 184 * the Cognito ID. 185 * @note If not loaded explicitly, the Cognito ID is loaded and stored in 186 * localStorage in the browser environment of a device. 187 * @api private 188 */ 189 getId: function getId(callback) { 190 var self = this; 191 if (typeof self.params.IdentityId === 'string') { 192 return callback(null, self.params.IdentityId); 193 } 194 195 self.cognito.getId(function(err, data) { 196 if (!err && data.IdentityId) { 197 self.params.IdentityId = data.IdentityId; 198 callback(null, data.IdentityId); 199 } else { 200 callback(err); 201 } 202 }); 203 }, 204 205 206 /** 207 * @api private 208 */ 209 loadCredentials: function loadCredentials(data, credentials) { 210 if (!data || !credentials) return; 211 credentials.expired = false; 212 credentials.accessKeyId = data.Credentials.AccessKeyId; 213 credentials.secretAccessKey = data.Credentials.SecretKey; 214 credentials.sessionToken = data.Credentials.SessionToken; 215 credentials.expireTime = data.Credentials.Expiration; 216 }, 217 218 /** 219 * @api private 220 */ 221 getCredentialsForIdentity: function getCredentialsForIdentity(callback) { 222 var self = this; 223 self.cognito.getCredentialsForIdentity(function(err, data) { 224 if (!err) { 225 self.cacheId(data); 226 self.data = data; 227 self.loadCredentials(self.data, self); 228 } else { 229 self.clearIdOnNotAuthorized(err); 230 } 231 callback(err); 232 }); 233 }, 234 235 /** 236 * @api private 237 */ 238 getCredentialsFromSTS: function getCredentialsFromSTS(callback) { 239 var self = this; 240 self.cognito.getOpenIdToken(function(err, data) { 241 if (!err) { 242 self.cacheId(data); 243 self.params.WebIdentityToken = data.Token; 244 self.webIdentityCredentials.refresh(function(webErr) { 245 if (!webErr) { 246 self.data = self.webIdentityCredentials.data; 247 self.sts.credentialsFrom(self.data, self); 248 } 249 callback(webErr); 250 }); 251 } else { 252 self.clearIdOnNotAuthorized(err); 253 callback(err); 254 } 255 }); 256 }, 257 258 /** 259 * @api private 260 */ 261 loadCachedId: function loadCachedId() { 262 var self = this; 263 264 // in the browser we source default IdentityId from localStorage 265 if (AWS.util.isBrowser() && !self.params.IdentityId) { 266 var id = self.getStorage('id'); 267 if (id && self.params.Logins) { 268 var actualProviders = Object.keys(self.params.Logins); 269 var cachedProviders = 270 (self.getStorage('providers') || '').split(','); 271 272 // only load ID if at least one provider used this ID before 273 var intersect = cachedProviders.filter(function(n) { 274 return actualProviders.indexOf(n) !== -1; 275 }); 276 if (intersect.length !== 0) { 277 self.params.IdentityId = id; 278 } 279 } else if (id) { 280 self.params.IdentityId = id; 281 } 282 } 283 }, 284 285 /** 286 * @api private 287 */ 288 createClients: function() { 289 this.webIdentityCredentials = this.webIdentityCredentials || 290 new AWS.WebIdentityCredentials(this.params); 291 this.cognito = this.cognito || 292 new CognitoIdentity({params: this.params}); 293 this.sts = this.sts || new STS(); 294 }, 295 296 /** 297 * @api private 298 */ 299 cacheId: function cacheId(data) { 300 this.identityId = data.IdentityId; 301 this.params.IdentityId = this.identityId; 302 303 // cache this IdentityId in browser localStorage if possible 304 if (AWS.util.isBrowser()) { 305 this.setStorage('id', data.IdentityId); 306 307 if (this.params.Logins) { 308 this.setStorage('providers', Object.keys(this.params.Logins).join(',')); 309 } 310 } 311 }, 312 313 /** 314 * @api private 315 */ 316 getStorage: function getStorage(key) { 317 return this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')]; 318 }, 319 320 /** 321 * @api private 322 */ 323 setStorage: function setStorage(key, val) { 324 try { 325 this.storage[this.localStorageKey[key] + this.params.IdentityPoolId + (this.params.LoginId || '')] = val; 326 } catch (_) {} 327 }, 328 329 /** 330 * @api private 331 */ 332 storage: (function() { 333 try { 334 return AWS.util.isBrowser() && window.localStorage !== null && typeof window.localStorage === 'object' ? 335 window.localStorage : {}; 336 } catch (_) { 337 return {}; 338 } 339 })() 340 });