@ttoss/cloud-auth 0.12.31 → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @ttoss/cloud-auth
2
2
 
3
- It's a library for creating AWS Cognito resources. It creates an user pool, identity pool, a client application, and others resources.
3
+ AWS Cognito authentication infrastructure as code. Creates user pools, identity pools, and Lambda triggers with CloudFormation.
4
4
 
5
5
  ## Installation
6
6
 
@@ -8,49 +8,184 @@ It's a library for creating AWS Cognito resources. It creates an user pool, iden
8
8
  pnpm add @ttoss/cloud-auth
9
9
  ```
10
10
 
11
- ## Quickstart
11
+ ## Quick Start
12
12
 
13
- Create a `cloudformation.ts` file in your project and export the template:
14
-
15
- ```typescript src/cloudformation.ts
13
+ ```typescript
14
+ // src/cloudformation.ts
16
15
  import { createAuthTemplate } from '@ttoss/cloud-auth';
17
16
 
18
- const template = createAuthTemplate();
17
+ export default createAuthTemplate();
18
+ ```
19
+
20
+ ## Core Features
19
21
 
20
- export default template;
22
+ ### User Pool Configuration
23
+
24
+ The template creates a secure user pool with email-based authentication by default:
25
+
26
+ ```typescript
27
+ const template = createAuthTemplate({
28
+ autoVerifiedAttributes: ['email'], // Default
29
+ usernameAttributes: ['email'], // Default
30
+ deletionProtection: 'ACTIVE', // Optional: ACTIVE | INACTIVE
31
+ schema: [
32
+ {
33
+ attributeDataType: 'String',
34
+ name: 'department',
35
+ required: false,
36
+ mutable: true,
37
+ stringAttributeConstraints: {
38
+ maxLength: '100',
39
+ minLength: '1',
40
+ },
41
+ },
42
+ ],
43
+ });
21
44
  ```
22
45
 
23
- ## Usage
46
+ ### Lambda Triggers
24
47
 
25
- ### Identity Pool
48
+ Customize authentication workflows with [AWS Cognito Lambda triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html). Lambda triggers accept either string ARNs or `Fn::GetAtt` CloudFormation references.
26
49
 
27
- #### Create an basic identity pool
50
+ #### Basic Lambda Trigger Setup
28
51
 
29
52
  ```typescript
30
53
  const template = createAuthTemplate({
31
- identityPool: {
32
- enabled: true, // false by default
33
- name: 'MyIdentityPool',
34
- allowUnauthenticatedIdentities: false, // false by default
54
+ lambdaTriggers: {
55
+ preSignUp: 'arn:aws:lambda:us-east-1:123456789:function:PreSignUp',
56
+ postConfirmation: { 'Fn::GetAtt': ['PostConfirmationFunction', 'Arn'] },
57
+ preTokenGeneration: { 'Fn::GetAtt': ['TokenCustomizerFunction', 'Arn'] },
35
58
  },
36
59
  });
37
60
  ```
38
61
 
39
- #### Create an identity pool with external roles
62
+ #### Complete Lambda Integration Example
63
+
64
+ Based on the [Terezinha Farm implementation](https://github.com/ttoss/ttoss/tree/main/terezinha-farm/auth), here's how to integrate Lambda functions with your auth template:
65
+
66
+ ```typescript
67
+ // src/cloudformation.ts
68
+ import { createAuthTemplate } from '@ttoss/cloud-auth';
69
+
70
+ export default () => {
71
+ const template = createAuthTemplate({
72
+ lambdaTriggers: {
73
+ postConfirmation: {
74
+ 'Fn::GetAtt': ['PostConfirmationLambdaFunction', 'Arn'],
75
+ },
76
+ },
77
+ });
78
+
79
+ // Add Lambda S3 parameters for Carlin deployment
80
+ template.Parameters = {
81
+ ...template.Parameters,
82
+ LambdaS3Bucket: { Type: 'String' },
83
+ LambdaS3Key: { Type: 'String' },
84
+ LambdaS3ObjectVersion: { Type: 'String' },
85
+ };
86
+
87
+ // Define Lambda function resource
88
+ template.Resources = {
89
+ ...template.Resources,
90
+ PostConfirmationLambdaFunction: {
91
+ Type: 'AWS::Lambda::Function',
92
+ Properties: {
93
+ Handler: 'triggers.postConfirmation',
94
+ Code: {
95
+ S3Bucket: { Ref: 'LambdaS3Bucket' },
96
+ S3Key: { Ref: 'LambdaS3Key' },
97
+ S3ObjectVersion: { Ref: 'LambdaS3ObjectVersion' },
98
+ },
99
+ Role: 'arn:aws:iam::account:role/lambda-execution-role',
100
+ Runtime: 'nodejs22.x',
101
+ },
102
+ },
103
+ };
104
+
105
+ return template;
106
+ };
107
+ ```
108
+
109
+ #### Lambda Function Implementation
110
+
111
+ Create your trigger functions following AWS Lambda handler patterns:
112
+
113
+ ```typescript
114
+ // src/triggers.ts
115
+ import type { PostConfirmationTriggerHandler } from 'aws-lambda';
116
+
117
+ export const postConfirmation: PostConfirmationTriggerHandler = async (
118
+ event
119
+ ) => {
120
+ const email = event.request.userAttributes.email;
121
+
122
+ // Custom logic: send welcome email, create user profile, etc.
123
+ console.log(`New user confirmed: ${email}`);
124
+
125
+ // Always return the event object
126
+ return event;
127
+ };
128
+ ```
129
+
130
+ #### Available Lambda Triggers
131
+
132
+ Check [Customizing user pool workflows with Lambda triggers](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html) for more information.
133
+
134
+ **Authentication Flow:**
135
+
136
+ - `preSignUp` - Validate signup data, auto-confirm users
137
+ - `postConfirmation` - Execute post-signup actions
138
+ - `preAuthentication` - Custom authentication validation
139
+ - `postAuthentication` - Track logins, update last seen
140
+
141
+ **Token Customization:**
142
+
143
+ - `preTokenGeneration` - Add custom claims, modify token content
144
+
145
+ **User Migration:**
146
+
147
+ - `userMigration` - Migrate users from external systems
148
+
149
+ **Custom Challenges:**
150
+
151
+ - `defineAuthChallenge` - Define custom authentication flows
152
+ - `createAuthChallenge` - Generate custom challenges
153
+ - `verifyAuthChallengeResponse` - Validate challenge responses
154
+
155
+ **Messaging:**
156
+
157
+ - `customMessage` - Customize email/SMS content
158
+ - `customEmailSender` - Third-party email providers
159
+ - `customSMSSender` - Third-party SMS providers
160
+
161
+ #### Deployment with Carlin
162
+
163
+ When using [Carlin deploy](https://ttoss.dev/docs/carlin/commands/deploy), Lambda functions are automatically built and uploaded to S3. Your `Handler` property should match your file structure:
164
+
165
+ ```
166
+ src/
167
+ └── triggers.ts # Handler: 'triggers.postConfirmation's
168
+ ```
169
+
170
+ The S3 parameters (`LambdaS3Bucket`, `LambdaS3Key`, `LambdaS3ObjectVersion`) are automatically injected by Carlin and referenced in your Lambda function's `Code` property.
171
+
172
+ ### Identity Pool
173
+
174
+ Enable federated identities for AWS resource access:
40
175
 
41
176
  ```typescript
42
177
  const template = createAuthTemplate({
43
178
  identityPool: {
44
179
  enabled: true,
45
- authenticatedRoleArn:
46
- 'arn:aws:iam::123456789012:role/MyIdentityPool_AuthenticatedRole',
47
- unauthenticatedRoleArn:
48
- 'arn:aws:iam::123456789012:role/MyIdentityPool_UnauthenticatedRole',
180
+ name: 'MyApp_IdentityPool',
181
+ allowUnauthenticatedIdentities: false,
49
182
  },
50
183
  });
51
184
  ```
52
185
 
53
- #### Create an identity pool with defined policies
186
+ #### Custom IAM Policies
187
+
188
+ Define specific permissions for authenticated and unauthenticated users:
54
189
 
55
190
  ```typescript
56
191
  const template = createAuthTemplate({
@@ -58,29 +193,14 @@ const template = createAuthTemplate({
58
193
  enabled: true,
59
194
  authenticatedPolicies: [
60
195
  {
61
- policyName: 'MyIdentityPool_AuthenticatedPolicy',
62
- policyDocument: {
196
+ PolicyName: 'S3Access',
197
+ PolicyDocument: {
63
198
  Version: '2012-10-17',
64
199
  Statement: [
65
200
  {
66
201
  Effect: 'Allow',
67
- Action: ['mobileanalytics:PutEvents', 'cognito-sync:*'],
68
- Resource: ['*'],
69
- },
70
- ],
71
- },
72
- },
73
- ],
74
- unauthenticatedPolicies: [
75
- {
76
- policyName: 'MyIdentityPool_UnauthenticatedPolicy',
77
- policyDocument: {
78
- Version: '2012-10-17',
79
- Statement: [
80
- {
81
- Effect: 'Deny',
82
- Action: ['*'],
83
- Resource: ['*'],
202
+ Action: ['s3:GetObject', 's3:PutObject'],
203
+ Resource: 'arn:aws:s3:::my-bucket/${aws:PrincipalTag/userId}/*',
84
204
  },
85
205
  ],
86
206
  },
@@ -90,53 +210,123 @@ const template = createAuthTemplate({
90
210
  });
91
211
  ```
92
212
 
93
- #### Using attributes for access control
213
+ #### Principal Tags for Access Control
94
214
 
95
- When you enable the identity pool, it maps the following [principal tags to handle access control](https://docs.aws.amazon.com/cognito/latest/developerguide/attributes-for-access-control.html) by default:
215
+ Enable [attribute-based access control](https://docs.aws.amazon.com/cognito/latest/developerguide/attributes-for-access-control.html) using JWT claims as IAM principal tags:
96
216
 
97
- ```yml
98
- PrincipalTags:
99
- appClientId: 'aud'
100
- userId: 'sub'
217
+ ```typescript
218
+ // Default principal tags
219
+ const template = createAuthTemplate({
220
+ identityPool: { enabled: true },
221
+ // Maps: appClientId → 'aud', userId → 'sub'
222
+ });
223
+
224
+ // Custom principal tags
225
+ const template = createAuthTemplate({
226
+ identityPool: {
227
+ enabled: true,
228
+ principalTags: {
229
+ department: 'custom:department',
230
+ role: 'custom:role',
231
+ userId: 'sub',
232
+ },
233
+ },
234
+ });
101
235
  ```
102
236
 
103
- This way you can use the `appClientId` and `userId` tags in your IAM policies by [controlling access for IAM principals](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_iam-tags.html#access_iam-tags_control-principals). For example:
237
+ Use principal tags in IAM policies for fine-grained access control:
104
238
 
105
239
  ```json
106
240
  {
107
- "Version": "2012-10-17",
108
- "Statement": [
109
- {
110
- "Effect": "Allow",
111
- "Action": "s3:GetObject*",
112
- "Resource": "arn:aws:s3:::*-${aws:PrincipalTag/userId}/*"
241
+ "Effect": "Allow",
242
+ "Action": "dynamodb:Query",
243
+ "Resource": "arn:aws:dynamodb:*:*:table/UserData",
244
+ "Condition": {
245
+ "StringEquals": {
246
+ "dynamodb:LeadingKeys": "${aws:PrincipalTag/userId}"
113
247
  }
114
- ]
248
+ }
115
249
  }
116
250
  ```
117
251
 
118
- You can change the default tags by passing the `principalTags` property and [other tokens](https://docs.aws.amazon.com/cognito/latest/developerguide/role-based-access-control.html#token-claims-for-role-based-access-control):
252
+ #### External IAM Roles
253
+
254
+ Use existing IAM roles instead of creating new ones:
119
255
 
120
256
  ```typescript
121
257
  const template = createAuthTemplate({
122
258
  identityPool: {
123
259
  enabled: true,
124
- principalTags: {
125
- appId: 'aud',
126
- username: 'sub',
127
- name: 'name',
128
- },
260
+ authenticatedRoleArn: 'arn:aws:iam::123456789012:role/AuthenticatedRole',
261
+ unauthenticatedRoleArn:
262
+ 'arn:aws:iam::123456789012:role:UnauthenticatedRole',
129
263
  },
130
264
  });
131
265
  ```
132
266
 
133
- If you want to disable the principal tags, you can pass the `principalTags` property with `false` value:
267
+ ## Template Outputs
268
+
269
+ The template provides these CloudFormation outputs for integration:
270
+
271
+ - **Region**: AWS region for Amplify Auth configuration
272
+ - **UserPoolId**: Cognito User Pool ID
273
+ - **AppClientId**: User Pool Client ID for applications
274
+ - **IdentityPoolId**: Identity Pool ID (when enabled)
275
+
276
+ Access outputs in other CloudFormation templates:
277
+
278
+ ```yaml
279
+ AuthConfig:
280
+ UserPoolId: !ImportValue MyAuthStack:UserPoolId
281
+ AppClientId: !ImportValue MyAuthStack:AppClientId
282
+ ```
283
+
284
+ ## Advanced Configuration
285
+
286
+ ### Custom User Attributes
287
+
288
+ Define application-specific user attributes with validation:
134
289
 
135
290
  ```typescript
136
291
  const template = createAuthTemplate({
137
- identityPool: {
138
- enabled: true,
139
- principalTags: false,
140
- },
292
+ schema: [
293
+ {
294
+ attributeDataType: 'Number',
295
+ name: 'employee_id',
296
+ required: true,
297
+ mutable: false,
298
+ numberAttributeConstraints: {
299
+ minValue: '1000',
300
+ maxValue: '99999',
301
+ },
302
+ },
303
+ {
304
+ attributeDataType: 'String',
305
+ name: 'department',
306
+ required: false,
307
+ mutable: true,
308
+ stringAttributeConstraints: {
309
+ minLength: '2',
310
+ maxLength: '50',
311
+ },
312
+ },
313
+ ],
141
314
  });
142
315
  ```
316
+
317
+ ### Production Considerations
318
+
319
+ For production deployments:
320
+
321
+ - Use `Fn::GetAtt` references instead of hardcoded ARNs for Lambda functions
322
+ - Enable deletion protection (`deletionProtection: 'ACTIVE'`) for production user pools
323
+ - Configure appropriate IAM roles with minimal required permissions for Lambda functions
324
+ - Implement proper error handling in Lambda triggers to prevent authentication failures
325
+ - Set up monitoring and alerting for authentication metrics
326
+ - Consider regional failover strategies
327
+
328
+ ## Related Resources
329
+
330
+ - [AWS Cognito User Pools Documentation](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html)
331
+ - [Lambda Triggers Reference](https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pools-working-with-lambda-triggers.html)
332
+ - [Attribute-Based Access Control](https://docs.aws.amazon.com/cognito/latest/developerguide/attributes-for-access-control.html)
package/dist/esm/index.js CHANGED
@@ -22,7 +22,9 @@ var createAuthTemplate = ({
22
22
  autoVerifiedAttributes = ["email"],
23
23
  identityPool,
24
24
  schema,
25
- usernameAttributes = ["email"]
25
+ usernameAttributes = ["email"],
26
+ lambdaTriggers,
27
+ deletionProtection
26
28
  } = {}) => {
27
29
  const AutoVerifiedAttributes = Array.isArray(autoVerifiedAttributes) && autoVerifiedAttributes.length > 0 ? autoVerifiedAttributes : [];
28
30
  const template = {
@@ -51,7 +53,10 @@ var createAuthTemplate = ({
51
53
  },
52
54
  UserPoolName: {
53
55
  Ref: "AWS::StackName"
54
- }
56
+ },
57
+ ...(deletionProtection && {
58
+ DeletionProtection: deletionProtection
59
+ })
55
60
  }
56
61
  },
57
62
  [CognitoUserPoolClientLogicalId]: {
@@ -135,7 +140,10 @@ var createAuthTemplate = ({
135
140
  StringAttributeConstraints
136
141
  };
137
142
  });
138
- template.Resources[CognitoUserPoolLogicalId].Properties.Schema = Schema;
143
+ template.Resources[CognitoUserPoolLogicalId].Properties = {
144
+ ...template.Resources[CognitoUserPoolLogicalId].Properties,
145
+ Schema
146
+ };
139
147
  }
140
148
  if (identityPool?.enabled) {
141
149
  template.Resources[CognitoIdentityPoolLogicalId] = {
@@ -156,7 +164,10 @@ var createAuthTemplate = ({
156
164
  }
157
165
  };
158
166
  if (identityPool.name) {
159
- template.Resources[CognitoIdentityPoolLogicalId].Properties.IdentityPoolName = identityPool.name;
167
+ template.Resources[CognitoIdentityPoolLogicalId].Properties = {
168
+ ...template.Resources[CognitoIdentityPoolLogicalId].Properties,
169
+ IdentityPoolName: identityPool.name
170
+ };
160
171
  }
161
172
  template.Resources.CognitoIdentityPoolRoleAttachment = {
162
173
  /**
@@ -203,11 +214,15 @@ var createAuthTemplate = ({
203
214
  }]
204
215
  }
205
216
  };
206
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.authenticated = {
207
- "Fn::GetAtt": [IdentityPoolAuthenticatedIAMRoleLogicalId, "Arn"]
208
- };
217
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
218
+ authenticated: {
219
+ "Fn::GetAtt": [IdentityPoolAuthenticatedIAMRoleLogicalId, "Arn"]
220
+ }
221
+ });
209
222
  } else {
210
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.authenticated = identityPool.authenticatedRoleArn;
223
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
224
+ authenticated: identityPool.authenticatedRoleArn
225
+ });
211
226
  }
212
227
  if (!identityPool.unauthenticatedRoleArn) {
213
228
  template.Resources[IdentityPoolUnauthenticatedIAMRoleLogicalId] = {
@@ -233,7 +248,7 @@ var createAuthTemplate = ({
233
248
  }
234
249
  }]
235
250
  },
236
- Policies: identityPool.authenticatedPolicies || [{
251
+ Policies: identityPool.unauthenticatedPolicies || [{
237
252
  PolicyName: "IdentityPoolUnauthenticatedIAMRolePolicyName",
238
253
  PolicyDocument: {
239
254
  Version: "2012-10-17",
@@ -242,11 +257,15 @@ var createAuthTemplate = ({
242
257
  }]
243
258
  }
244
259
  };
245
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.unauthenticated = {
246
- "Fn::GetAtt": [IdentityPoolUnauthenticatedIAMRoleLogicalId, "Arn"]
247
- };
260
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
261
+ unauthenticated: {
262
+ "Fn::GetAtt": [IdentityPoolUnauthenticatedIAMRoleLogicalId, "Arn"]
263
+ }
264
+ });
248
265
  } else {
249
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.unauthenticated = identityPool.unauthenticatedRoleArn;
266
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
267
+ unauthenticated: identityPool.unauthenticatedRoleArn
268
+ });
250
269
  }
251
270
  if (identityPool.principalTags || identityPool.principalTags === void 0) {
252
271
  const PrincipalTags = (() => {
@@ -272,23 +291,82 @@ var createAuthTemplate = ({
272
291
  }
273
292
  };
274
293
  }
275
- if (!template.Outputs) {
276
- template.Outputs = {};
277
- }
278
- template.Outputs.IdentityPoolId = {
279
- Description: "You use this value on Amplify Auth `identityPoolId`.",
280
- Value: {
281
- Ref: CognitoIdentityPoolLogicalId
282
- },
283
- Export: {
284
- Name: {
285
- "Fn::Join": [":", [{
286
- Ref: "AWS::StackName"
287
- }, "CognitoIdentityPoolId"]]
294
+ template.Outputs = {
295
+ ...template.Outputs,
296
+ IdentityPoolId: {
297
+ Description: "You use this value on Amplify Auth `identityPoolId`.",
298
+ Value: {
299
+ Ref: CognitoIdentityPoolLogicalId
300
+ },
301
+ Export: {
302
+ Name: {
303
+ "Fn::Join": [":", [{
304
+ Ref: "AWS::StackName"
305
+ }, "CognitoIdentityPoolId"]]
306
+ }
288
307
  }
289
308
  }
290
309
  };
291
310
  }
311
+ if (lambdaTriggers) {
312
+ const LambdaConfig = {};
313
+ if (lambdaTriggers.preSignUp) {
314
+ LambdaConfig.PreSignUp = lambdaTriggers.preSignUp;
315
+ }
316
+ if (lambdaTriggers.postConfirmation) {
317
+ LambdaConfig.PostConfirmation = lambdaTriggers.postConfirmation;
318
+ }
319
+ if (lambdaTriggers.preAuthentication) {
320
+ LambdaConfig.PreAuthentication = lambdaTriggers.preAuthentication;
321
+ }
322
+ if (lambdaTriggers.postAuthentication) {
323
+ LambdaConfig.PostAuthentication = lambdaTriggers.postAuthentication;
324
+ }
325
+ if (lambdaTriggers.defineAuthChallenge) {
326
+ LambdaConfig.DefineAuthChallenge = lambdaTriggers.defineAuthChallenge;
327
+ }
328
+ if (lambdaTriggers.createAuthChallenge) {
329
+ LambdaConfig.CreateAuthChallenge = lambdaTriggers.createAuthChallenge;
330
+ }
331
+ if (lambdaTriggers.verifyAuthChallengeResponse) {
332
+ LambdaConfig.VerifyAuthChallengeResponse = lambdaTriggers.verifyAuthChallengeResponse;
333
+ }
334
+ if (lambdaTriggers.preTokenGeneration) {
335
+ LambdaConfig.PreTokenGeneration = lambdaTriggers.preTokenGeneration;
336
+ }
337
+ if (lambdaTriggers.userMigration) {
338
+ LambdaConfig.UserMigration = lambdaTriggers.userMigration;
339
+ }
340
+ if (lambdaTriggers.customMessage) {
341
+ LambdaConfig.CustomMessage = lambdaTriggers.customMessage;
342
+ }
343
+ if (lambdaTriggers.customEmailSender) {
344
+ LambdaConfig.CustomEmailSender = lambdaTriggers.customEmailSender;
345
+ }
346
+ if (lambdaTriggers.customSMSSender) {
347
+ LambdaConfig.CustomSMSSender = lambdaTriggers.customSMSSender;
348
+ }
349
+ if (Object.keys(LambdaConfig).length > 0) {
350
+ template.Resources[CognitoUserPoolLogicalId].Properties = {
351
+ ...template.Resources[CognitoUserPoolLogicalId].Properties,
352
+ LambdaConfig
353
+ };
354
+ }
355
+ for (const [key, lambdaTrigger] of Object.entries(LambdaConfig)) {
356
+ const permissionLogicalId = `${key}PermissionFor${CognitoUserPoolLogicalId}`.slice(0, 255);
357
+ template.Resources[permissionLogicalId] = {
358
+ Type: "AWS::Lambda::Permission",
359
+ Properties: {
360
+ Action: "lambda:InvokeFunction",
361
+ FunctionName: lambdaTrigger,
362
+ Principal: "cognito-idp.amazonaws.com",
363
+ SourceArn: {
364
+ "Fn::GetAtt": [CognitoUserPoolLogicalId, "Arn"]
365
+ }
366
+ }
367
+ };
368
+ }
369
+ }
292
370
  return template;
293
371
  };
294
372
  createAuthTemplate.CognitoUserPoolLogicalId = CognitoUserPoolLogicalId;
package/dist/index.d.mts CHANGED
@@ -1,40 +1,56 @@
1
- import { Policy } from '@ttoss/cloudformation';
1
+ import { Policy, CloudFormationGetAtt, CloudFormationTemplate } from '@ttoss/cloudformation';
2
2
 
3
3
  declare const PASSWORD_MINIMUM_LENGTH = 8;
4
4
 
5
+ type SchemaAttribute = {
6
+ attributeDataType?: 'Boolean' | 'DateTime' | 'Number' | 'String';
7
+ developerOnlyAttribute?: boolean;
8
+ mutable?: boolean;
9
+ name?: string;
10
+ numberAttributeConstraints?: {
11
+ maxValue?: string;
12
+ minValue?: string;
13
+ };
14
+ required?: boolean;
15
+ stringAttributeConstraints?: {
16
+ maxLength: string;
17
+ minLength: string;
18
+ };
19
+ };
20
+ type IdentityPoolConfig = {
21
+ enabled?: boolean;
22
+ name?: string;
23
+ allowUnauthenticatedIdentities?: boolean;
24
+ authenticatedRoleArn?: string;
25
+ authenticatedPolicies?: Policy[];
26
+ unauthenticatedRoleArn?: string;
27
+ unauthenticatedPolicies?: Policy[];
28
+ principalTags?: Record<string, string> | boolean;
29
+ };
30
+ type LambdaTriggers = {
31
+ preSignUp?: string | CloudFormationGetAtt;
32
+ postConfirmation?: string | CloudFormationGetAtt;
33
+ preAuthentication?: string | CloudFormationGetAtt;
34
+ postAuthentication?: string | CloudFormationGetAtt;
35
+ defineAuthChallenge?: string | CloudFormationGetAtt;
36
+ createAuthChallenge?: string | CloudFormationGetAtt;
37
+ verifyAuthChallengeResponse?: string | CloudFormationGetAtt;
38
+ preTokenGeneration?: string | CloudFormationGetAtt;
39
+ userMigration?: string | CloudFormationGetAtt;
40
+ customMessage?: string | CloudFormationGetAtt;
41
+ customEmailSender?: string | CloudFormationGetAtt;
42
+ customSMSSender?: string | CloudFormationGetAtt;
43
+ };
44
+ type CreateAuthTemplateParams = {
45
+ autoVerifiedAttributes?: Array<'email' | 'phone_number'> | null | false;
46
+ identityPool?: IdentityPoolConfig;
47
+ schema?: SchemaAttribute[];
48
+ usernameAttributes?: Array<'email' | 'phone_number'> | null;
49
+ lambdaTriggers?: LambdaTriggers;
50
+ deletionProtection?: 'ACTIVE' | 'INACTIVE';
51
+ };
5
52
  declare const createAuthTemplate: {
6
- ({ autoVerifiedAttributes, identityPool, schema, usernameAttributes, }?: {
7
- autoVerifiedAttributes?: Array<"email" | "phone_number"> | null | false;
8
- identityPool?: {
9
- enabled?: boolean;
10
- name?: string;
11
- allowUnauthenticatedIdentities?: boolean;
12
- authenticatedRoleArn?: string;
13
- authenticatedPolicies?: Policy[];
14
- unauthenticatedRoleArn?: string;
15
- unauthenticatedPolicies?: Policy[];
16
- principalTags?: Record<string, string> | boolean;
17
- };
18
- /**
19
- * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-schemaattribute.html
20
- */
21
- schema?: {
22
- attributeDataType?: "Boolean" | "DateTime" | "Number" | "String";
23
- developerOnlyAttribute?: boolean;
24
- mutable?: boolean;
25
- name?: string;
26
- numberAttributeConstraints?: {
27
- maxValue?: string;
28
- minValue?: string;
29
- };
30
- required?: boolean;
31
- stringAttributeConstraints?: {
32
- maxLength: string;
33
- minLength: string;
34
- };
35
- }[];
36
- usernameAttributes?: Array<"email" | "phone_number"> | null;
37
- }): any;
53
+ ({ autoVerifiedAttributes, identityPool, schema, usernameAttributes, lambdaTriggers, deletionProtection, }?: CreateAuthTemplateParams): CloudFormationTemplate;
38
54
  CognitoUserPoolLogicalId: string;
39
55
  CognitoUserPoolClientLogicalId: string;
40
56
  CognitoIdentityPoolLogicalId: string;
package/dist/index.d.ts CHANGED
@@ -1,40 +1,56 @@
1
- import { Policy } from '@ttoss/cloudformation';
1
+ import { Policy, CloudFormationGetAtt, CloudFormationTemplate } from '@ttoss/cloudformation';
2
2
 
3
3
  declare const PASSWORD_MINIMUM_LENGTH = 8;
4
4
 
5
+ type SchemaAttribute = {
6
+ attributeDataType?: 'Boolean' | 'DateTime' | 'Number' | 'String';
7
+ developerOnlyAttribute?: boolean;
8
+ mutable?: boolean;
9
+ name?: string;
10
+ numberAttributeConstraints?: {
11
+ maxValue?: string;
12
+ minValue?: string;
13
+ };
14
+ required?: boolean;
15
+ stringAttributeConstraints?: {
16
+ maxLength: string;
17
+ minLength: string;
18
+ };
19
+ };
20
+ type IdentityPoolConfig = {
21
+ enabled?: boolean;
22
+ name?: string;
23
+ allowUnauthenticatedIdentities?: boolean;
24
+ authenticatedRoleArn?: string;
25
+ authenticatedPolicies?: Policy[];
26
+ unauthenticatedRoleArn?: string;
27
+ unauthenticatedPolicies?: Policy[];
28
+ principalTags?: Record<string, string> | boolean;
29
+ };
30
+ type LambdaTriggers = {
31
+ preSignUp?: string | CloudFormationGetAtt;
32
+ postConfirmation?: string | CloudFormationGetAtt;
33
+ preAuthentication?: string | CloudFormationGetAtt;
34
+ postAuthentication?: string | CloudFormationGetAtt;
35
+ defineAuthChallenge?: string | CloudFormationGetAtt;
36
+ createAuthChallenge?: string | CloudFormationGetAtt;
37
+ verifyAuthChallengeResponse?: string | CloudFormationGetAtt;
38
+ preTokenGeneration?: string | CloudFormationGetAtt;
39
+ userMigration?: string | CloudFormationGetAtt;
40
+ customMessage?: string | CloudFormationGetAtt;
41
+ customEmailSender?: string | CloudFormationGetAtt;
42
+ customSMSSender?: string | CloudFormationGetAtt;
43
+ };
44
+ type CreateAuthTemplateParams = {
45
+ autoVerifiedAttributes?: Array<'email' | 'phone_number'> | null | false;
46
+ identityPool?: IdentityPoolConfig;
47
+ schema?: SchemaAttribute[];
48
+ usernameAttributes?: Array<'email' | 'phone_number'> | null;
49
+ lambdaTriggers?: LambdaTriggers;
50
+ deletionProtection?: 'ACTIVE' | 'INACTIVE';
51
+ };
5
52
  declare const createAuthTemplate: {
6
- ({ autoVerifiedAttributes, identityPool, schema, usernameAttributes, }?: {
7
- autoVerifiedAttributes?: Array<"email" | "phone_number"> | null | false;
8
- identityPool?: {
9
- enabled?: boolean;
10
- name?: string;
11
- allowUnauthenticatedIdentities?: boolean;
12
- authenticatedRoleArn?: string;
13
- authenticatedPolicies?: Policy[];
14
- unauthenticatedRoleArn?: string;
15
- unauthenticatedPolicies?: Policy[];
16
- principalTags?: Record<string, string> | boolean;
17
- };
18
- /**
19
- * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-schemaattribute.html
20
- */
21
- schema?: {
22
- attributeDataType?: "Boolean" | "DateTime" | "Number" | "String";
23
- developerOnlyAttribute?: boolean;
24
- mutable?: boolean;
25
- name?: string;
26
- numberAttributeConstraints?: {
27
- maxValue?: string;
28
- minValue?: string;
29
- };
30
- required?: boolean;
31
- stringAttributeConstraints?: {
32
- maxLength: string;
33
- minLength: string;
34
- };
35
- }[];
36
- usernameAttributes?: Array<"email" | "phone_number"> | null;
37
- }): any;
53
+ ({ autoVerifiedAttributes, identityPool, schema, usernameAttributes, lambdaTriggers, deletionProtection, }?: CreateAuthTemplateParams): CloudFormationTemplate;
38
54
  CognitoUserPoolLogicalId: string;
39
55
  CognitoUserPoolClientLogicalId: string;
40
56
  CognitoIdentityPoolLogicalId: string;
package/dist/index.js CHANGED
@@ -54,7 +54,9 @@ var createAuthTemplate = ({
54
54
  autoVerifiedAttributes = ["email"],
55
55
  identityPool,
56
56
  schema,
57
- usernameAttributes = ["email"]
57
+ usernameAttributes = ["email"],
58
+ lambdaTriggers,
59
+ deletionProtection
58
60
  } = {}) => {
59
61
  const AutoVerifiedAttributes = Array.isArray(autoVerifiedAttributes) && autoVerifiedAttributes.length > 0 ? autoVerifiedAttributes : [];
60
62
  const template = {
@@ -83,7 +85,10 @@ var createAuthTemplate = ({
83
85
  },
84
86
  UserPoolName: {
85
87
  Ref: "AWS::StackName"
86
- }
88
+ },
89
+ ...(deletionProtection && {
90
+ DeletionProtection: deletionProtection
91
+ })
87
92
  }
88
93
  },
89
94
  [CognitoUserPoolClientLogicalId]: {
@@ -167,7 +172,10 @@ var createAuthTemplate = ({
167
172
  StringAttributeConstraints
168
173
  };
169
174
  });
170
- template.Resources[CognitoUserPoolLogicalId].Properties.Schema = Schema;
175
+ template.Resources[CognitoUserPoolLogicalId].Properties = {
176
+ ...template.Resources[CognitoUserPoolLogicalId].Properties,
177
+ Schema
178
+ };
171
179
  }
172
180
  if (identityPool?.enabled) {
173
181
  template.Resources[CognitoIdentityPoolLogicalId] = {
@@ -188,7 +196,10 @@ var createAuthTemplate = ({
188
196
  }
189
197
  };
190
198
  if (identityPool.name) {
191
- template.Resources[CognitoIdentityPoolLogicalId].Properties.IdentityPoolName = identityPool.name;
199
+ template.Resources[CognitoIdentityPoolLogicalId].Properties = {
200
+ ...template.Resources[CognitoIdentityPoolLogicalId].Properties,
201
+ IdentityPoolName: identityPool.name
202
+ };
192
203
  }
193
204
  template.Resources.CognitoIdentityPoolRoleAttachment = {
194
205
  /**
@@ -235,11 +246,15 @@ var createAuthTemplate = ({
235
246
  }]
236
247
  }
237
248
  };
238
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.authenticated = {
239
- "Fn::GetAtt": [IdentityPoolAuthenticatedIAMRoleLogicalId, "Arn"]
240
- };
249
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
250
+ authenticated: {
251
+ "Fn::GetAtt": [IdentityPoolAuthenticatedIAMRoleLogicalId, "Arn"]
252
+ }
253
+ });
241
254
  } else {
242
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.authenticated = identityPool.authenticatedRoleArn;
255
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
256
+ authenticated: identityPool.authenticatedRoleArn
257
+ });
243
258
  }
244
259
  if (!identityPool.unauthenticatedRoleArn) {
245
260
  template.Resources[IdentityPoolUnauthenticatedIAMRoleLogicalId] = {
@@ -265,7 +280,7 @@ var createAuthTemplate = ({
265
280
  }
266
281
  }]
267
282
  },
268
- Policies: identityPool.authenticatedPolicies || [{
283
+ Policies: identityPool.unauthenticatedPolicies || [{
269
284
  PolicyName: "IdentityPoolUnauthenticatedIAMRolePolicyName",
270
285
  PolicyDocument: {
271
286
  Version: "2012-10-17",
@@ -274,11 +289,15 @@ var createAuthTemplate = ({
274
289
  }]
275
290
  }
276
291
  };
277
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.unauthenticated = {
278
- "Fn::GetAtt": [IdentityPoolUnauthenticatedIAMRoleLogicalId, "Arn"]
279
- };
292
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
293
+ unauthenticated: {
294
+ "Fn::GetAtt": [IdentityPoolUnauthenticatedIAMRoleLogicalId, "Arn"]
295
+ }
296
+ });
280
297
  } else {
281
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles.unauthenticated = identityPool.unauthenticatedRoleArn;
298
+ Object.assign(template.Resources.CognitoIdentityPoolRoleAttachment.Properties?.Roles, {
299
+ unauthenticated: identityPool.unauthenticatedRoleArn
300
+ });
282
301
  }
283
302
  if (identityPool.principalTags || identityPool.principalTags === void 0) {
284
303
  const PrincipalTags = (() => {
@@ -304,23 +323,82 @@ var createAuthTemplate = ({
304
323
  }
305
324
  };
306
325
  }
307
- if (!template.Outputs) {
308
- template.Outputs = {};
309
- }
310
- template.Outputs.IdentityPoolId = {
311
- Description: "You use this value on Amplify Auth `identityPoolId`.",
312
- Value: {
313
- Ref: CognitoIdentityPoolLogicalId
314
- },
315
- Export: {
316
- Name: {
317
- "Fn::Join": [":", [{
318
- Ref: "AWS::StackName"
319
- }, "CognitoIdentityPoolId"]]
326
+ template.Outputs = {
327
+ ...template.Outputs,
328
+ IdentityPoolId: {
329
+ Description: "You use this value on Amplify Auth `identityPoolId`.",
330
+ Value: {
331
+ Ref: CognitoIdentityPoolLogicalId
332
+ },
333
+ Export: {
334
+ Name: {
335
+ "Fn::Join": [":", [{
336
+ Ref: "AWS::StackName"
337
+ }, "CognitoIdentityPoolId"]]
338
+ }
320
339
  }
321
340
  }
322
341
  };
323
342
  }
343
+ if (lambdaTriggers) {
344
+ const LambdaConfig = {};
345
+ if (lambdaTriggers.preSignUp) {
346
+ LambdaConfig.PreSignUp = lambdaTriggers.preSignUp;
347
+ }
348
+ if (lambdaTriggers.postConfirmation) {
349
+ LambdaConfig.PostConfirmation = lambdaTriggers.postConfirmation;
350
+ }
351
+ if (lambdaTriggers.preAuthentication) {
352
+ LambdaConfig.PreAuthentication = lambdaTriggers.preAuthentication;
353
+ }
354
+ if (lambdaTriggers.postAuthentication) {
355
+ LambdaConfig.PostAuthentication = lambdaTriggers.postAuthentication;
356
+ }
357
+ if (lambdaTriggers.defineAuthChallenge) {
358
+ LambdaConfig.DefineAuthChallenge = lambdaTriggers.defineAuthChallenge;
359
+ }
360
+ if (lambdaTriggers.createAuthChallenge) {
361
+ LambdaConfig.CreateAuthChallenge = lambdaTriggers.createAuthChallenge;
362
+ }
363
+ if (lambdaTriggers.verifyAuthChallengeResponse) {
364
+ LambdaConfig.VerifyAuthChallengeResponse = lambdaTriggers.verifyAuthChallengeResponse;
365
+ }
366
+ if (lambdaTriggers.preTokenGeneration) {
367
+ LambdaConfig.PreTokenGeneration = lambdaTriggers.preTokenGeneration;
368
+ }
369
+ if (lambdaTriggers.userMigration) {
370
+ LambdaConfig.UserMigration = lambdaTriggers.userMigration;
371
+ }
372
+ if (lambdaTriggers.customMessage) {
373
+ LambdaConfig.CustomMessage = lambdaTriggers.customMessage;
374
+ }
375
+ if (lambdaTriggers.customEmailSender) {
376
+ LambdaConfig.CustomEmailSender = lambdaTriggers.customEmailSender;
377
+ }
378
+ if (lambdaTriggers.customSMSSender) {
379
+ LambdaConfig.CustomSMSSender = lambdaTriggers.customSMSSender;
380
+ }
381
+ if (Object.keys(LambdaConfig).length > 0) {
382
+ template.Resources[CognitoUserPoolLogicalId].Properties = {
383
+ ...template.Resources[CognitoUserPoolLogicalId].Properties,
384
+ LambdaConfig
385
+ };
386
+ }
387
+ for (const [key, lambdaTrigger] of Object.entries(LambdaConfig)) {
388
+ const permissionLogicalId = `${key}PermissionFor${CognitoUserPoolLogicalId}`.slice(0, 255);
389
+ template.Resources[permissionLogicalId] = {
390
+ Type: "AWS::Lambda::Permission",
391
+ Properties: {
392
+ Action: "lambda:InvokeFunction",
393
+ FunctionName: lambdaTrigger,
394
+ Principal: "cognito-idp.amazonaws.com",
395
+ SourceArn: {
396
+ "Fn::GetAtt": [CognitoUserPoolLogicalId, "Arn"]
397
+ }
398
+ }
399
+ };
400
+ }
401
+ }
324
402
  return template;
325
403
  };
326
404
  createAuthTemplate.CognitoUserPoolLogicalId = CognitoUserPoolLogicalId;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/cloud-auth",
3
- "version": "0.12.31",
3
+ "version": "0.13.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,7 +19,7 @@
19
19
  ],
20
20
  "sideEffects": false,
21
21
  "dependencies": {
22
- "@ttoss/cloudformation": "^0.10.20"
22
+ "@ttoss/cloudformation": "^0.11.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/jest": "^30.0.0",