@ttoss/cloud-auth 0.6.4 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttoss/cloud-auth",
3
- "version": "0.6.4",
3
+ "version": "0.7.1",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/esm/index.js",
6
6
  "scripts": {
@@ -9,16 +9,16 @@
9
9
  },
10
10
  "typings": "./dist/index.d.ts",
11
11
  "dependencies": {
12
- "@ttoss/cloudformation": "^0.5.4"
12
+ "@ttoss/cloudformation": "^0.6.1"
13
13
  },
14
14
  "devDependencies": {
15
- "@ttoss/config": "^1.28.2",
15
+ "@ttoss/config": "^1.28.3",
16
16
  "@types/jest": "^29.4.0",
17
- "jest": "^29.4.3",
17
+ "jest": "^29.5.0",
18
18
  "typescript": "^4.9.5"
19
19
  },
20
20
  "publishConfig": {
21
21
  "access": "public"
22
22
  },
23
- "gitHead": "7ce61c5deab1deb32f9f2b573fe2ae6cebb778cd"
23
+ "gitHead": "56e8cfde36a962deaa5514453618280699824b4f"
24
24
  }
package/src/template.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { CloudFormationTemplate } from '@ttoss/cloudformation';
2
1
  import { PASSWORD_MINIMUM_LENGTH } from './config';
2
+ import type { CloudFormationTemplate, Policy } from '@ttoss/cloudformation';
3
3
 
4
4
  const CognitoUserPoolLogicalId = 'CognitoUserPool';
5
5
 
@@ -7,41 +7,46 @@ const CognitoUserPoolClientLogicalId = 'CognitoUserPoolClient';
7
7
 
8
8
  const CognitoIdentityPoolLogicalId = 'CognitoIdentityPool';
9
9
 
10
- type Role =
11
- | string
12
- | {
13
- 'Fn::ImportValue': string;
14
- };
10
+ const IdentityPoolAuthenticatedIAMRoleLogicalId =
11
+ 'IdentityPoolAuthenticatedIAMRole';
12
+
13
+ const IdentityPoolUnauthenticatedIAMRoleLogicalId =
14
+ 'IdentityPoolUnauthenticatedIAMRole';
15
+
16
+ export const DenyStatement = {
17
+ Effect: 'Deny' as const,
18
+ Action: ['*'],
19
+ Resource: ['*'],
20
+ };
15
21
 
16
22
  export const createAuthTemplate = ({
17
23
  autoVerifiedAttributes = ['email'],
18
- identityPool = true,
19
- roles,
24
+ identityPool,
20
25
  schema,
21
26
  usernameAttributes = ['email'],
22
27
  }: {
23
28
  autoVerifiedAttributes?: Array<'email' | 'phone_number'> | null | false;
24
- identityPool?: boolean;
25
- roles?: {
26
- authenticated?: Role;
27
- unauthenticated?: Role;
29
+ identityPool?: {
30
+ enabled?: boolean;
31
+ authenticatedPolicies?: Policy[];
32
+ unauthenticatedPolicies?: Policy[];
28
33
  };
29
34
  /**
30
35
  * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-cognito-userpool-schemaattribute.html
31
36
  */
32
37
  schema?: {
33
- AttributeDataType?: 'Boolean' | 'DateTime' | 'Number' | 'String';
34
- DeveloperOnlyAttribute?: boolean;
35
- Mutable?: boolean;
36
- Name?: string;
37
- NumberAttributeConstraints?: {
38
- MaxValue?: string;
39
- MinValue?: string;
38
+ attributeDataType?: 'Boolean' | 'DateTime' | 'Number' | 'String';
39
+ developerOnlyAttribute?: boolean;
40
+ mutable?: boolean;
41
+ name?: string;
42
+ numberAttributeConstraints?: {
43
+ maxValue?: string;
44
+ minValue?: string;
40
45
  };
41
- Required?: boolean;
42
- StringAttributeConstraints?: {
43
- MaxLength: string;
44
- MinLength: string;
46
+ required?: boolean;
47
+ stringAttributeConstraints?: {
48
+ maxLength: string;
49
+ minLength: string;
45
50
  };
46
51
  }[];
47
52
  usernameAttributes?: Array<'email' | 'phone_number'> | null;
@@ -56,6 +61,7 @@ export const createAuthTemplate = ({
56
61
  Resources: {
57
62
  [CognitoUserPoolLogicalId]: {
58
63
  Type: 'AWS::Cognito::UserPool',
64
+ DeletionPolicy: 'Retain',
59
65
  Properties: {
60
66
  AutoVerifiedAttributes,
61
67
  Policies: {
@@ -68,7 +74,6 @@ export const createAuthTemplate = ({
68
74
  TemporaryPasswordValidityDays: 30,
69
75
  },
70
76
  },
71
- Schema: schema,
72
77
  UsernameAttributes: usernameAttributes,
73
78
  UsernameConfiguration: {
74
79
  CaseSensitive: false,
@@ -126,7 +131,41 @@ export const createAuthTemplate = ({
126
131
  },
127
132
  };
128
133
 
129
- if (identityPool) {
134
+ if (schema) {
135
+ const Schema = schema.map((attribute) => {
136
+ let NumberAttributeConstraints = undefined;
137
+
138
+ if (attribute.numberAttributeConstraints) {
139
+ NumberAttributeConstraints = {
140
+ MaxValue: attribute.numberAttributeConstraints?.maxValue,
141
+ MinValue: attribute.numberAttributeConstraints?.minValue,
142
+ };
143
+ }
144
+
145
+ let StringAttributeConstraints = undefined;
146
+
147
+ if (attribute.stringAttributeConstraints) {
148
+ StringAttributeConstraints = {
149
+ MaxLength: attribute.stringAttributeConstraints?.maxLength,
150
+ MinLength: attribute.stringAttributeConstraints?.minLength,
151
+ };
152
+ }
153
+
154
+ return {
155
+ AttributeDataType: attribute.attributeDataType,
156
+ DeveloperOnlyAttribute: attribute.developerOnlyAttribute,
157
+ Mutable: attribute.mutable,
158
+ Name: attribute.name,
159
+ NumberAttributeConstraints,
160
+ Required: attribute.required,
161
+ StringAttributeConstraints,
162
+ };
163
+ });
164
+
165
+ template.Resources[CognitoUserPoolLogicalId].Properties.Schema = Schema;
166
+ }
167
+
168
+ if (identityPool?.enabled) {
130
169
  /**
131
170
  * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-identitypool.html
132
171
  */
@@ -147,20 +186,99 @@ export const createAuthTemplate = ({
147
186
  },
148
187
  };
149
188
 
150
- if (roles) {
151
- /**
152
- * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-identitypoolroleattachment.html
153
- */
154
- template.Resources.CognitoIdentityPoolRoleAttachment = {
155
- Type: 'AWS::Cognito::IdentityPoolRoleAttachment',
156
- Properties: {
157
- IdentityPoolId: {
158
- Ref: CognitoIdentityPoolLogicalId,
189
+ template.Resources[IdentityPoolAuthenticatedIAMRoleLogicalId] = {
190
+ Type: 'AWS::IAM::Role',
191
+ Properties: {
192
+ AssumeRolePolicyDocument: {
193
+ Version: '2012-10-17' as const,
194
+ Statement: [
195
+ {
196
+ Effect: 'Allow' as const,
197
+ Principal: {
198
+ Federated: 'cognito-identity.amazonaws.com',
199
+ },
200
+ Action: ['sts:AssumeRoleWithWebIdentity', 'sts:TagSession'],
201
+ Condition: {
202
+ StringEquals: {
203
+ 'cognito-identity.amazonaws.com:aud': {
204
+ Ref: CognitoIdentityPoolLogicalId,
205
+ },
206
+ },
207
+ 'ForAnyValue:StringLike': {
208
+ 'cognito-identity.amazonaws.com:amr': 'authenticated',
209
+ },
210
+ },
211
+ },
212
+ ],
213
+ },
214
+ Policies: identityPool.authenticatedPolicies || [
215
+ {
216
+ PolicyName: 'IdentityPoolAuthenticatedIAMRolePolicyName',
217
+ PolicyDocument: {
218
+ Version: '2012-10-17' as const,
219
+ Statement: [DenyStatement],
220
+ },
221
+ },
222
+ ],
223
+ },
224
+ };
225
+
226
+ template.Resources[IdentityPoolUnauthenticatedIAMRoleLogicalId] = {
227
+ Type: 'AWS::IAM::Role',
228
+ Properties: {
229
+ AssumeRolePolicyDocument: {
230
+ Version: '2012-10-17' as const,
231
+ Statement: [
232
+ {
233
+ Effect: 'Allow' as const,
234
+ Principal: {
235
+ Federated: 'cognito-identity.amazonaws.com',
236
+ },
237
+ Action: 'sts:AssumeRoleWithWebIdentity',
238
+ Condition: {
239
+ StringEquals: {
240
+ 'cognito-identity.amazonaws.com:aud': {
241
+ Ref: CognitoIdentityPoolLogicalId,
242
+ },
243
+ },
244
+ 'ForAnyValue:StringLike': {
245
+ 'cognito-identity.amazonaws.com:amr': 'unauthenticated',
246
+ },
247
+ },
248
+ },
249
+ ],
250
+ },
251
+ Policies: identityPool.authenticatedPolicies || [
252
+ {
253
+ PolicyName: 'IdentityPoolUnauthenticatedIAMRolePolicyName',
254
+ PolicyDocument: {
255
+ Version: '2012-10-17' as const,
256
+ Statement: [DenyStatement],
257
+ },
258
+ },
259
+ ],
260
+ },
261
+ };
262
+
263
+ /**
264
+ * https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-cognito-identitypoolroleattachment.html
265
+ */
266
+ template.Resources.CognitoIdentityPoolRoleAttachment = {
267
+ Type: 'AWS::Cognito::IdentityPoolRoleAttachment',
268
+ Properties: {
269
+ IdentityPoolId: {
270
+ Ref: CognitoIdentityPoolLogicalId,
271
+ },
272
+ Roles: {
273
+ authenticated: {
274
+ 'Fn::GetAtt': [IdentityPoolAuthenticatedIAMRoleLogicalId, 'Arn'],
275
+ },
276
+ unauthenticated: {
277
+ 'Fn::GetAtt': [IdentityPoolUnauthenticatedIAMRoleLogicalId, 'Arn'],
159
278
  },
160
- Roles: roles,
161
279
  },
162
- };
163
- }
280
+ },
281
+ };
164
282
 
165
283
  if (!template.Outputs) {
166
284
  template.Outputs = {};
@@ -1,80 +1,100 @@
1
1
  import { createAuthTemplate } from '../../src';
2
2
 
3
- test('do not add schema if not provided', () => {
4
- const template = createAuthTemplate();
5
- expect(template.Resources.CognitoUserPool.Properties.Schema).toBeUndefined();
6
- });
3
+ describe('user pool', () => {
4
+ test('do not add schema if not provided', () => {
5
+ const template = createAuthTemplate();
6
+ expect(
7
+ template.Resources.CognitoUserPool.Properties.Schema
8
+ ).toBeUndefined();
9
+ });
7
10
 
8
- test('add schema if provided', () => {
9
- const schema = [
10
- {
11
- AttributeDataType: 'String' as const,
12
- DeveloperOnlyAttribute: false,
13
- Mutable: true,
14
- Name: 'email',
15
- Required: true,
16
- StringAttributeConstraints: {
17
- MaxLength: '2048',
18
- MinLength: '0',
11
+ test('add schema if provided', () => {
12
+ const schema = [
13
+ {
14
+ attributeDataType: 'String' as const,
15
+ developerOnlyAttribute: false,
16
+ mutable: true,
17
+ name: 'email',
18
+ required: true,
19
+ stringAttributeConstraints: {
20
+ maxLength: '2048',
21
+ minLength: '0',
22
+ },
19
23
  },
20
- },
21
- ];
22
-
23
- const template = createAuthTemplate({ schema });
24
- expect(template.Resources.CognitoUserPool.Properties.Schema).toEqual(schema);
25
- });
24
+ ];
26
25
 
27
- test('should have autoVerifiedAttributes equal email by default', () => {
28
- const template = createAuthTemplate();
29
- expect(
30
- template.Resources.CognitoUserPool.Properties.AutoVerifiedAttributes
31
- ).toEqual(['email']);
32
- });
26
+ const template = createAuthTemplate({ schema });
27
+ expect(template.Resources.CognitoUserPool.Properties.Schema).toEqual([
28
+ {
29
+ AttributeDataType: 'String',
30
+ DeveloperOnlyAttribute: false,
31
+ Mutable: true,
32
+ Name: 'email',
33
+ Required: true,
34
+ StringAttributeConstraints: {
35
+ MaxLength: '2048',
36
+ MinLength: '0',
37
+ },
38
+ },
39
+ ]);
40
+ });
33
41
 
34
- test.each([[], null, false])(
35
- 'should have autoVerifiedAttributes undefined: %p',
36
- (autoVerifiedAttributes: any) => {
37
- const template = createAuthTemplate({ autoVerifiedAttributes });
42
+ test('should have autoVerifiedAttributes equal email by default', () => {
43
+ const template = createAuthTemplate();
38
44
  expect(
39
45
  template.Resources.CognitoUserPool.Properties.AutoVerifiedAttributes
40
- ).toEqual([]);
41
- }
42
- );
46
+ ).toEqual(['email']);
47
+ });
43
48
 
44
- test.each([true, undefined])(
45
- 'should have identity pool by default or true: %p',
46
- (identityPool) => {
47
- const template = createAuthTemplate({ identityPool });
48
- expect(template.Resources.CognitoIdentityPool).toBeDefined();
49
- expect(template.Outputs?.IdentityPoolId).toBeDefined();
50
- }
51
- );
49
+ test('default usernameAttributes should be email', () => {
50
+ const template = createAuthTemplate();
51
+ expect(
52
+ template.Resources.CognitoUserPool.Properties.UsernameAttributes
53
+ ).toEqual(['email']);
54
+ });
52
55
 
53
- test('should not have identity pool if false', () => {
54
- const template = createAuthTemplate({ identityPool: false });
55
- expect(template.Resources.CognitoIdentityPool).toBeUndefined();
56
- expect(template.Outputs?.IdentityPoolId).toBeUndefined();
57
- });
56
+ test.each([[], null, false])(
57
+ 'should have autoVerifiedAttributes undefined: %p',
58
+ (autoVerifiedAttributes: any) => {
59
+ const template = createAuthTemplate({ autoVerifiedAttributes });
60
+ expect(
61
+ template.Resources.CognitoUserPool.Properties.AutoVerifiedAttributes
62
+ ).toEqual([]);
63
+ }
64
+ );
58
65
 
59
- test('should have identity pool role attachment with roles', () => {
60
- const roles = {
61
- authenticated: 'arn:aws:iam::123456789012:role/authenticated',
62
- unauthenticated: 'arn:aws:iam::123456789012:role/unauthenticated',
63
- };
64
- const template = createAuthTemplate({ roles });
65
- expect(
66
- template.Resources.CognitoIdentityPoolRoleAttachment.Properties.Roles
67
- ).toEqual(roles);
66
+ test('should retain user pool', () => {
67
+ const template = createAuthTemplate();
68
+ expect(template.Resources.CognitoUserPool.DeletionPolicy).toEqual('Retain');
69
+ });
68
70
  });
69
71
 
70
- test('should not have identity pool role attachment without roles', () => {
71
- const template = createAuthTemplate();
72
- expect(template.Resources.CognitoIdentityPoolRoleAttachment).toBeUndefined();
73
- });
72
+ describe('identity pool', () => {
73
+ test.each([false, undefined])(
74
+ 'should not have identity pool by default or false: %p',
75
+ (enabled) => {
76
+ const template = createAuthTemplate({ identityPool: { enabled } });
77
+ expect(template.Resources.CognitoIdentityPool).not.toBeDefined();
78
+ expect(template.Outputs?.IdentityPoolId).not.toBeDefined();
79
+ }
80
+ );
81
+
82
+ test('should have identity pool if false', () => {
83
+ const template = createAuthTemplate({
84
+ identityPool: {
85
+ enabled: true,
86
+ },
87
+ });
88
+ expect(template.Resources.CognitoIdentityPool).toBeDefined();
89
+ expect(template.Outputs?.IdentityPoolId).toBeDefined();
90
+ });
74
91
 
75
- test('default usernameAttributes should be email', () => {
76
- const template = createAuthTemplate();
77
- expect(
78
- template.Resources.CognitoUserPool.Properties.UsernameAttributes
79
- ).toEqual(['email']);
92
+ test('should have identity pool role attachment', () => {
93
+ const template = createAuthTemplate({
94
+ identityPool: {
95
+ enabled: true,
96
+ },
97
+ });
98
+ expect(template.Resources.CognitoIdentityPoolRoleAttachment).toBeDefined();
99
+ });
80
100
  });