-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathidentitypool.ts
500 lines (437 loc) · 16 KB
/
identitypool.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
import {
CfnIdentityPool,
} from '@aws-cdk/aws-cognito';
import {
IOpenIdConnectProvider,
ISamlProvider,
Role,
FederatedPrincipal,
IRole,
} from '@aws-cdk/aws-iam';
import {
Resource,
IResource,
Stack,
ArnFormat,
Lazy,
} from '@aws-cdk/core';
import {
Construct,
} from 'constructs';
import {
IdentityPoolRoleAttachment,
IdentityPoolRoleMapping,
} from './identitypool-role-attachment';
import {
IUserPoolAuthenticationProvider,
} from './identitypool-user-pool-authentication-provider';
/**
* Represents a Cognito IdentityPool
*/
export interface IIdentityPool extends IResource {
/**
* The id of the Identity Pool in the format REGION:GUID
* @attribute
*/
readonly identityPoolId: string;
/**
* The ARN of the Identity Pool
* @attribute
*/
readonly identityPoolArn: string;
/**
* Name of the Identity Pool
* @attribute
*/
readonly identityPoolName: string
}
/**
* Props for the IdentityPool construct
*/
export interface IdentityPoolProps {
/**
* The name of the Identity Pool
* @default - automatically generated name by CloudFormation at deploy time
*/
readonly identityPoolName?: string;
/**
* The Default Role to be assumed by Authenticated Users
* @default - A Default Authenticated Role will be added
*/
readonly authenticatedRole?: IRole;
/**
* The Default Role to be assumed by Unauthenticated Users
* @default - A Default Unauthenticated Role will be added
*/
readonly unauthenticatedRole?: IRole;
/**
* Wwhether the identity pool supports unauthenticated logins
* @default - false
*/
readonly allowUnauthenticatedIdentities?: boolean
/**
* Rules for mapping roles to users
* @default - no Role Mappings
*/
readonly roleMappings?: IdentityPoolRoleMapping[];
/**
* Enables the Basic (Classic) authentication flow
* @default - Classic Flow not allowed
*/
readonly allowClassicFlow?: boolean;
/**
* Authentication providers for using in identity pool.
* @default - No Authentication Providers passed directly to Identity Pool
*/
readonly authenticationProviders?: IdentityPoolAuthenticationProviders
}
/**
* Types of Identity Pool Login Providers
*/
export enum IdentityPoolProviderType {
/** Facebook Provider type */
FACEBOOK = 'Facebook',
/** Google Provider Type */
GOOGLE = 'Google',
/** Amazon Provider Type */
AMAZON = 'Amazon',
/** Apple Provider Type */
APPLE = 'Apple',
/** Twitter Provider Type */
TWITTER = 'Twitter',
/** Digits Provider Type */
DIGITS = 'Digits',
/** Open Id Provider Type */
OPEN_ID = 'OpenId',
/** Saml Provider Type */
SAML = 'Saml',
/** User Pool Provider Type */
USER_POOL = 'UserPool',
/** Custom Provider Type */
CUSTOM = 'Custom',
}
/**
* Keys for Login Providers - correspond to client id's of respective federation identity providers
*/
export class IdentityPoolProviderUrl {
/** Facebook Provider Url */
public static readonly FACEBOOK = new IdentityPoolProviderUrl(IdentityPoolProviderType.FACEBOOK, 'graph.facebook.com');
/** Google Provider Url */
public static readonly GOOGLE = new IdentityPoolProviderUrl(IdentityPoolProviderType.GOOGLE, 'accounts.google.com');
/** Amazon Provider Url */
public static readonly AMAZON = new IdentityPoolProviderUrl(IdentityPoolProviderType.AMAZON, 'www.amazon.com');
/** Apple Provider Url */
public static readonly APPLE = new IdentityPoolProviderUrl(IdentityPoolProviderType.APPLE, 'appleid.apple.com');
/** Twitter Provider Url */
public static readonly TWITTER = new IdentityPoolProviderUrl(IdentityPoolProviderType.TWITTER, 'api.twitter.com');
/** Digits Provider Url */
public static readonly DIGITS = new IdentityPoolProviderUrl(IdentityPoolProviderType.DIGITS, 'www.digits.com');
/** OpenId Provider Url */
public static openId(url: string): IdentityPoolProviderUrl {
return new IdentityPoolProviderUrl(IdentityPoolProviderType.OPEN_ID, url);
}
/** Saml Provider Url */
public static saml(url: string): IdentityPoolProviderUrl {
return new IdentityPoolProviderUrl(IdentityPoolProviderType.SAML, url);
}
/** User Pool Provider Url */
public static userPool(url: string): IdentityPoolProviderUrl {
return new IdentityPoolProviderUrl(IdentityPoolProviderType.USER_POOL, url);
}
/** Custom Provider Url */
public static custom(url: string): IdentityPoolProviderUrl {
return new IdentityPoolProviderUrl(IdentityPoolProviderType.CUSTOM, url);
}
constructor(
/** type of Provider Url */
public readonly type: IdentityPoolProviderType,
/** value of Provider Url */
public readonly value: string,
) {}
}
/**
* Login Provider for Identity Federation using Amazon Credentials
*/
export interface IdentityPoolAmazonLoginProvider {
/**
* App Id for Amazon Identity Federation
*/
readonly appId: string
}
/**
* Login Provider for Identity Federation using Facebook Credentials
*/
export interface IdentityPoolFacebookLoginProvider {
/**
* App Id for Facebook Identity Federation
*/
readonly appId: string
}
/**
* Login Provider for Identity Federation using Apple Credentials
*/
export interface IdentityPoolAppleLoginProvider {
/**
* App Id for Apple Identity Federation
*/
readonly servicesId: string
}
/**
* Login Provider for Identity Federation using Google Credentials
*/
export interface IdentityPoolGoogleLoginProvider {
/**
* App Id for Google Identity Federation
*/
readonly clientId: string
}
/**
* Login Provider for Identity Federation using Twitter Credentials
*/
export interface IdentityPoolTwitterLoginProvider {
/**
* App Id for Twitter Identity Federation
*/
readonly consumerKey: string
/**
* App Secret for Twitter Identity Federation
*/
readonly consumerSecret: string
}
/**
* Login Provider for Identity Federation using Digits Credentials
*/
export interface IdentityPoolDigitsLoginProvider extends IdentityPoolTwitterLoginProvider {}
/**
* External Identity Providers To Connect to User Pools and Identity Pools
*/
export interface IdentityPoolProviders {
/** App Id for Facebook Identity Federation
* @default - No Facebook Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly facebook?: IdentityPoolFacebookLoginProvider
/** Client Id for Google Identity Federation
* @default - No Google Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly google?: IdentityPoolGoogleLoginProvider
/** App Id for Amazon Identity Federation
* @default - No Amazon Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly amazon?: IdentityPoolAmazonLoginProvider
/** Services Id for Apple Identity Federation
* @default - No Apple Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly apple?: IdentityPoolAppleLoginProvider
/** Consumer Key and Secret for Twitter Identity Federation
* @default - No Twitter Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly twitter?: IdentityPoolTwitterLoginProvider
/** Consumer Key and Secret for Digits Identity Federation
* @default - No Digits Authentication Provider used without OpenIdConnect or a User Pool
*/
readonly digits?: IdentityPoolDigitsLoginProvider
}
/**
* Authentication providers for using in identity pool.
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/external-identity-providers.html
*/
export interface IdentityPoolAuthenticationProviders extends IdentityPoolProviders {
/**
* The User Pool Authentication Providers associated with this Identity Pool
* @default - no User Pools Associated
*/
readonly userPools?: IUserPoolAuthenticationProvider[];
/**
* The OpenIdConnect Provider associated with this Identity Pool
* @default - no OpenIdConnectProvider
*/
readonly openIdConnectProviders?: IOpenIdConnectProvider[];
/**
* The Security Assertion Markup Language Provider associated with this Identity Pool
* @default - no SamlProvider
*/
readonly samlProviders?: ISamlProvider[];
/**
* The Developer Provider Name to associate with this Identity Pool
* @default - no Custom Provider
*/
readonly customProvider?: string;
}
/**
* Define a Cognito Identity Pool
*
* @resource AWS::Cognito::IdentityPool
*/
export class IdentityPool extends Resource implements IIdentityPool {
/**
* Import an existing Identity Pool from its id
*/
public static fromIdentityPoolId(scope: Construct, id: string, identityPoolId: string): IIdentityPool {
const identityPoolArn = Stack.of(scope).formatArn({
service: 'cognito-identity',
resource: 'identitypool',
resourceName: identityPoolId,
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
});
return IdentityPool.fromIdentityPoolArn(scope, id, identityPoolArn);
}
/**
* Import an existing Identity Pool from its Arn
*/
public static fromIdentityPoolArn(scope: Construct, id: string, identityPoolArn: string): IIdentityPool {
const pool = Stack.of(scope).splitArn(identityPoolArn, ArnFormat.SLASH_RESOURCE_NAME);
const res = pool.resourceName || '';
if (!res) {
throw new Error('Invalid Identity Pool ARN');
}
const idParts = res.split(':');
if (!(idParts.length === 2)) throw new Error('Invalid Identity Pool Id: Identity Pool Ids must follow the format <region>:<id>');
if (idParts[0] !== pool.region) throw new Error('Invalid Identity Pool Id: Region in Identity Pool Id must match stack region');
class ImportedIdentityPool extends Resource implements IIdentityPool {
public readonly identityPoolId = res;
public readonly identityPoolArn = identityPoolArn;
public readonly identityPoolName: string
constructor() {
super(scope, id, {
account: pool.account,
region: pool.region,
});
this.identityPoolName = this.physicalName;
}
}
return new ImportedIdentityPool();
}
/**
* The id of the Identity Pool in the format REGION:GUID
* @attribute
*/
public readonly identityPoolId: string;
/**
* The ARN of the Identity Pool
* @attribute
*/
public readonly identityPoolArn: string;
/**
* The name of the Identity Pool
* @attribute
*/
public readonly identityPoolName: string;
/**
* Default role for authenticated users
*/
public readonly authenticatedRole: IRole;
/**
* Default role for unauthenticated users
*/
public readonly unauthenticatedRole: IRole;
/**
* List of Identity Providers added in constructor for use with property overrides
*/
private cognitoIdentityProviders: CfnIdentityPool.CognitoIdentityProviderProperty[] = [];
/**
* Running count of added role attachments
*/
private roleAttachmentCount: number = 0;
constructor(scope: Construct, id: string, props:IdentityPoolProps = {}) {
super(scope, id, {
physicalName: props.identityPoolName,
});
const authProviders: IdentityPoolAuthenticationProviders = props.authenticationProviders || {};
const providers = authProviders.userPools ? authProviders.userPools.map(userPool => userPool.bind(this, this)) : undefined;
if (providers && providers.length) this.cognitoIdentityProviders = providers;
const openIdConnectProviderArns = authProviders.openIdConnectProviders ?
authProviders.openIdConnectProviders.map(openIdProvider =>
openIdProvider.openIdConnectProviderArn,
) : undefined;
const samlProviderArns = authProviders.samlProviders ?
authProviders.samlProviders.map(samlProvider =>
samlProvider.samlProviderArn,
) : undefined;
let supportedLoginProviders:any = {};
if (authProviders.amazon) supportedLoginProviders[IdentityPoolProviderUrl.AMAZON.value] = authProviders.amazon.appId;
if (authProviders.facebook) supportedLoginProviders[IdentityPoolProviderUrl.FACEBOOK.value] = authProviders.facebook.appId;
if (authProviders.google) supportedLoginProviders[IdentityPoolProviderUrl.GOOGLE.value] = authProviders.google.clientId;
if (authProviders.apple) supportedLoginProviders[IdentityPoolProviderUrl.APPLE.value] = authProviders.apple.servicesId;
if (authProviders.twitter) supportedLoginProviders[IdentityPoolProviderUrl.TWITTER.value] = `${authProviders.twitter.consumerKey};${authProviders.twitter.consumerSecret}`;
if (authProviders.digits) supportedLoginProviders[IdentityPoolProviderUrl.DIGITS.value] = `${authProviders.digits.consumerKey};${authProviders.digits.consumerSecret}`;
if (!Object.keys(supportedLoginProviders).length) supportedLoginProviders = undefined;
const cfnIdentityPool = new CfnIdentityPool(this, 'Resource', {
allowUnauthenticatedIdentities: props.allowUnauthenticatedIdentities ? true : false,
allowClassicFlow: props.allowClassicFlow,
identityPoolName: this.physicalName,
developerProviderName: authProviders.customProvider,
openIdConnectProviderArns,
samlProviderArns,
supportedLoginProviders,
cognitoIdentityProviders: Lazy.any({ produce: () => this.cognitoIdentityProviders }),
});
this.identityPoolName = cfnIdentityPool.attrName;
this.identityPoolId = cfnIdentityPool.ref;
this.identityPoolArn = Stack.of(scope).formatArn({
service: 'cognito-identity',
resource: 'identitypool',
resourceName: this.identityPoolId,
arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
});
this.authenticatedRole = props.authenticatedRole ? props.authenticatedRole : this.configureDefaultRole('Authenticated');
this.unauthenticatedRole = props.unauthenticatedRole ? props.unauthenticatedRole : this.configureDefaultRole('Unauthenticated');
const attachment = new IdentityPoolRoleAttachment(this, 'DefaultRoleAttachment', {
identityPool: this,
authenticatedRole: this.authenticatedRole,
unauthenticatedRole: this.unauthenticatedRole,
roleMappings: props.roleMappings,
});
// This added by the original author, but it's causing cyclic dependencies.
// Don't know why this was added in the first place, but I'm disabling it for now and if
// no complaints come from this, we're probably safe to remove it altogether.
// attachment.node.addDependency(this);
Array.isArray(attachment);
}
/**
* Add a User Pool to the IdentityPool and configure User Pool Client to handle identities
*/
public addUserPoolAuthentication(userPool: IUserPoolAuthenticationProvider): void {
const providers = userPool.bind(this, this);
this.cognitoIdentityProviders = this.cognitoIdentityProviders.concat(providers);
}
/**
* Adds Role Mappings to Identity Pool
*/
public addRoleMappings(...roleMappings: IdentityPoolRoleMapping[]): void {
if (!roleMappings || !roleMappings.length) return;
this.roleAttachmentCount++;
const name = 'RoleMappingAttachment' + this.roleAttachmentCount.toString();
const attachment = new IdentityPoolRoleAttachment(this, name, {
identityPool: this,
authenticatedRole: this.authenticatedRole,
unauthenticatedRole: this.unauthenticatedRole,
roleMappings,
});
// This added by the original author, but it's causing cyclic dependencies.
// Don't know why this was added in the first place, but I'm disabling it for now and if
// no complaints come from this, we're probably safe to remove it altogether.
// attachment.node.addDependency(this);
Array.isArray(attachment);
}
/**
* Configure Default Roles For Identity Pool
*/
private configureDefaultRole(type: string): IRole {
const assumedBy = this.configureDefaultGrantPrincipal(type.toLowerCase());
const role = new Role(this, `${type}Role`, {
description: `Default ${type} Role for Identity Pool ${this.identityPoolName}`,
assumedBy,
});
return role;
}
private configureDefaultGrantPrincipal(type: string) {
return new FederatedPrincipal('cognito-identity.amazonaws.com', {
'StringEquals': {
'cognito-identity.amazonaws.com:aud': this.identityPoolId,
},
'ForAnyValue:StringLike': {
'cognito-identity.amazonaws.com:amr': type,
},
}, 'sts:AssumeRoleWithWebIdentity');
}
}