@gugananuvem/aws-local-simulator 1.0.14 → 1.0.16

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.
@@ -3,18 +3,18 @@
3
3
  * Simula User Pools, Identity Pools, Autenticação, Tokens JWT
4
4
  */
5
5
 
6
- const crypto = require('crypto');
7
- const jwt = require('jsonwebtoken');
8
- const { v4: uuidv4 } = require('uuid');
9
- const logger = require('../../utils/logger');
10
- const LocalStore = require('../../utils/local-store');
11
- const path = require('path');
12
- const { CloudTrailAudit } = require('../../utils/cloudtrail-audit');
6
+ const crypto = require("crypto");
7
+ const jwt = require("jsonwebtoken");
8
+ const { v4: uuidv4 } = require("uuid");
9
+ const logger = require("../../utils/logger");
10
+ const LocalStore = require("../../utils/local-store");
11
+ const path = require("path");
12
+ const { CloudTrailAudit } = require("../../utils/cloudtrail-audit");
13
13
 
14
14
  class CognitoSimulator {
15
15
  constructor(config) {
16
16
  this.config = config;
17
- this.dataDir = path.join(process.env.AWS_LOCAL_SIMULATOR_DATA_DIR, 'cognito');
17
+ this.dataDir = path.join(process.env.AWS_LOCAL_SIMULATOR_DATA_DIR, "cognito");
18
18
  this.store = new LocalStore(this.dataDir);
19
19
  this.userPools = new Map();
20
20
  this.identityPools = new Map();
@@ -22,31 +22,86 @@ class CognitoSimulator {
22
22
  this.sessions = new Map();
23
23
  this.refreshTokens = new Map();
24
24
  this.accessTokens = new Map();
25
- this.jwtSecret = crypto.randomBytes(64).toString('hex');
26
- this.audit = new CloudTrailAudit('cognito-idp.amazonaws.com');
25
+ this.jwtSecret = crypto.randomBytes(64).toString("hex");
26
+ this.audit = new CloudTrailAudit("cognito-idp.amazonaws.com");
27
+ this.lambdaSimulator = null;
28
+ this.customAuthSessions = new Map();
29
+ }
30
+
31
+ setLambdaSimulator(lambdaSimulator) {
32
+ this.lambdaSimulator = lambdaSimulator;
33
+ }
34
+
35
+ _warnUnregisteredTriggers(pool) {
36
+ const triggers = pool.LambdaTriggers || {};
37
+ for (const [triggerName, fnName] of Object.entries(triggers)) {
38
+ if (fnName && !this.lambdaSimulator?.getLambda(fnName)) {
39
+ logger.warn(`⚠️ Cognito pool "${pool.Name}": trigger "${triggerName}" references Lambda "${fnName}" which is not registered`);
40
+ }
41
+ }
42
+ }
43
+
44
+ _buildTriggerEvent(triggerSource, userPool, user, clientId, requestFields) {
45
+ return {
46
+ version: "1",
47
+ triggerSource,
48
+ region: "us-east-1",
49
+ userPoolId: userPool.Id,
50
+ userName: user.Username,
51
+ callerContext: {
52
+ awsSdkVersion: "aws-sdk-unknown-unknown",
53
+ clientId,
54
+ },
55
+ request: { ...requestFields },
56
+ response: {},
57
+ };
58
+ }
59
+
60
+ // Returns userAttributes in the flat key/value format that real Cognito sends to triggers
61
+ _triggerUserAttributes(user) {
62
+ return {
63
+ sub: user.UserId,
64
+ "cognito:user_status": user.UserStatus,
65
+ ...user.Attributes,
66
+ };
67
+ }
68
+
69
+ async _invokeTrigger(userPool, triggerName, event) {
70
+ const fnName = userPool.LambdaTriggers?.[triggerName];
71
+ if (!fnName) {
72
+ return null;
73
+ }
74
+
75
+ if (!this.lambdaSimulator || !this.lambdaSimulator.getLambda(fnName)) {
76
+ logger.warn(`⚠️ Cognito trigger "${triggerName}": Lambda "${fnName}" is not available, skipping`);
77
+ return null;
78
+ }
79
+
80
+ const result = await this.lambdaSimulator.invoke(fnName, event, "RequestResponse");
81
+ return result.Payload;
27
82
  }
28
83
 
29
84
  async initialize() {
30
- logger.debug('Inicializando Cognito Simulator...');
85
+ logger.debug("Inicializando Cognito Simulator...");
31
86
  this.loadUserPools();
32
87
  this.loadIdentityPools();
33
88
  this.loadUsers();
34
89
  this.loadSessions();
35
-
90
+
36
91
  logger.debug(`✅ Cognito Simulator inicializado com ${this.userPools.size} user pools, ${this.identityPools.size} identity pools, ${this.users.size} usuários`);
37
92
  }
38
93
 
39
94
  // ============ User Pool Operations ============
40
95
 
41
96
  createUserPool(params) {
42
- const { PoolName, Policies, LambdaConfig, AutoVerifiedAttributes, AliasAttributes, UsernameAttributes, MfaConfiguration } = params;
43
-
44
- const poolId = `local_${PoolName}_${Date.now()}`;
97
+ const { PoolName, Policies, LambdaConfig, AutoVerifiedAttributes, AliasAttributes, UsernameAttributes, MfaConfiguration, UserPoolId } = params;
98
+
99
+ const poolId = UserPoolId ? UserPoolId : `local_${PoolName}_${Date.now()}`;
45
100
  const userPool = {
46
101
  Id: poolId,
47
102
  Name: PoolName,
48
103
  Arn: `arn:aws:cognito:local:000000000000:userpool/${poolId}`,
49
- Status: 'ACTIVE',
104
+ Status: "ACTIVE",
50
105
  CreationDate: new Date().toISOString(),
51
106
  LastModifiedDate: new Date().toISOString(),
52
107
  Policies: Policies || {
@@ -55,28 +110,28 @@ class CognitoSimulator {
55
110
  RequireUppercase: true,
56
111
  RequireLowercase: true,
57
112
  RequireNumbers: true,
58
- RequireSymbols: false
59
- }
113
+ RequireSymbols: false,
114
+ },
60
115
  },
61
116
  LambdaConfig: LambdaConfig || {},
62
- AutoVerifiedAttributes: AutoVerifiedAttributes || ['email'],
117
+ AutoVerifiedAttributes: AutoVerifiedAttributes || ["email"],
63
118
  AliasAttributes: AliasAttributes || [],
64
- UsernameAttributes: UsernameAttributes || ['email'],
65
- MfaConfiguration: MfaConfiguration || 'OFF',
119
+ UsernameAttributes: UsernameAttributes || ["email"],
120
+ MfaConfiguration: MfaConfiguration || "OFF",
66
121
  EstimatedNumberOfUsers: 0,
67
122
  Users: [],
68
123
  Clients: new Map(),
69
124
  Groups: new Map(),
70
125
  IdentityProviders: new Map(),
71
- ResourceServers: new Map()
126
+ ResourceServers: new Map(),
72
127
  };
73
-
128
+
74
129
  this.userPools.set(poolId, userPool);
75
130
  this.persistUserPools();
76
-
131
+
77
132
  logger.debug(`✅ User Pool criado: ${PoolName} (${poolId})`);
78
- this.audit.record({ eventName: 'CreateUserPool', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { poolName: PoolName } });
79
-
133
+ this.audit.record({ eventName: "CreateUserPool", readOnly: false, resources: [{ ARN: userPool.Arn, type: "AWS::Cognito::UserPool" }], requestParameters: { poolName: PoolName } });
134
+
80
135
  return {
81
136
  UserPool: {
82
137
  Id: userPool.Id,
@@ -86,44 +141,44 @@ class CognitoSimulator {
86
141
  CreationDate: userPool.CreationDate,
87
142
  LastModifiedDate: userPool.LastModifiedDate,
88
143
  MfaConfiguration: userPool.MfaConfiguration,
89
- EstimatedNumberOfUsers: 0
90
- }
144
+ EstimatedNumberOfUsers: 0,
145
+ },
91
146
  };
92
147
  }
93
148
 
94
149
  listUserPools(params = {}) {
95
150
  const { MaxResults = 60, NextToken } = params;
96
151
  let userPools = Array.from(this.userPools.values());
97
-
152
+
98
153
  if (NextToken) {
99
154
  const startIndex = parseInt(NextToken);
100
155
  userPools = userPools.slice(startIndex);
101
156
  }
102
-
157
+
103
158
  const results = userPools.slice(0, MaxResults);
104
159
  const nextToken = results.length === MaxResults ? String(MaxResults) : null;
105
-
160
+
106
161
  return {
107
- UserPools: results.map(pool => ({
162
+ UserPools: results.map((pool) => ({
108
163
  Id: pool.Id,
109
164
  Name: pool.Name,
110
165
  Arn: pool.Arn,
111
166
  Status: pool.Status,
112
167
  CreationDate: pool.CreationDate,
113
- LastModifiedDate: pool.LastModifiedDate
168
+ LastModifiedDate: pool.LastModifiedDate,
114
169
  })),
115
- NextToken: nextToken
170
+ NextToken: nextToken,
116
171
  };
117
172
  }
118
173
 
119
174
  describeUserPool(params) {
120
175
  const { UserPoolId } = params;
121
176
  const userPool = this.userPools.get(UserPoolId);
122
-
177
+
123
178
  if (!userPool) {
124
179
  throw new Error(`User pool ${UserPoolId} not found`);
125
180
  }
126
-
181
+
127
182
  return {
128
183
  UserPool: {
129
184
  Id: userPool.Id,
@@ -138,8 +193,8 @@ class CognitoSimulator {
138
193
  AliasAttributes: userPool.AliasAttributes,
139
194
  UsernameAttributes: userPool.UsernameAttributes,
140
195
  MfaConfiguration: userPool.MfaConfiguration,
141
- EstimatedNumberOfUsers: userPool.Users.length
142
- }
196
+ EstimatedNumberOfUsers: userPool.Users.length,
197
+ },
143
198
  };
144
199
  }
145
200
 
@@ -151,7 +206,7 @@ class CognitoSimulator {
151
206
  throw new Error(`User pool ${UserPoolId} not found`);
152
207
  }
153
208
 
154
- let users = Array.from(this.users.values()).filter(u => u.UserPoolId === UserPoolId);
209
+ let users = Array.from(this.users.values()).filter((u) => u.UserPoolId === UserPoolId);
155
210
 
156
211
  if (PaginationToken) {
157
212
  const startIndex = parseInt(PaginationToken);
@@ -162,15 +217,15 @@ class CognitoSimulator {
162
217
  const nextToken = results.length === Limit && users.length > Limit ? String(Limit) : null;
163
218
 
164
219
  return {
165
- Users: results.map(u => ({
220
+ Users: results.map((u) => ({
166
221
  Username: u.Username,
167
222
  UserStatus: u.UserStatus,
168
223
  Enabled: u.Enabled,
169
224
  UserCreateDate: u.CreatedDate,
170
225
  UserLastModifiedDate: u.LastModifiedDate,
171
- Attributes: this.formatUserAttributes(u.Attributes)
226
+ Attributes: this.formatUserAttributes(u.Attributes),
172
227
  })),
173
- PaginationToken: nextToken
228
+ PaginationToken: nextToken,
174
229
  };
175
230
  }
176
231
 
@@ -184,12 +239,12 @@ class CognitoSimulator {
184
239
  const results = clients.slice(0, MaxResults);
185
240
 
186
241
  return {
187
- UserPoolClients: results.map(c => ({
242
+ UserPoolClients: results.map((c) => ({
188
243
  ClientId: c.ClientId,
189
244
  ClientName: c.ClientName,
190
- UserPoolId: c.UserPoolId
245
+ UserPoolId: c.UserPoolId,
191
246
  })),
192
- NextToken: results.length === MaxResults && clients.length > MaxResults ? String(MaxResults) : null
247
+ NextToken: results.length === MaxResults && clients.length > MaxResults ? String(MaxResults) : null,
193
248
  };
194
249
  }
195
250
 
@@ -215,15 +270,54 @@ class CognitoSimulator {
215
270
  const { ClientId, Username } = params;
216
271
  const userPool = this.findUserPoolByClientId(ClientId);
217
272
  if (!userPool) throw new Error(`Client ${ClientId} not found`);
218
- return { CodeDeliveryDetails: { Destination: 'test@example.com', DeliveryMedium: 'EMAIL', AttributeName: 'email' } };
273
+
274
+ const user = this.findUserByUsername(Username, ClientId);
275
+ if (!user) throw new Error(`User not found: ${Username}`);
276
+
277
+ if (user.UserStatus !== "CONFIRMED") {
278
+ const err = new Error("Cannot reset password for the user as there is no registered/verified email or phone_number");
279
+ err.code = "InvalidParameterException";
280
+ throw err;
281
+ }
282
+
283
+ const resetCode = Math.floor(100000 + Math.random() * 900000).toString();
284
+ user.PasswordResetCode = resetCode;
285
+ this.persistUsers();
286
+
287
+ const userEmail = user.Attributes.email || Username;
288
+ logger.info(`📧 [COGNITO] Código de redefinição de senha para "${Username}": ${resetCode}`);
289
+
290
+ return {
291
+ CodeDeliveryDetails: {
292
+ Destination: userEmail,
293
+ DeliveryMedium: "EMAIL",
294
+ AttributeName: "email",
295
+ },
296
+ };
219
297
  }
220
298
 
221
299
  confirmForgotPassword(params = {}) {
222
300
  const { ClientId, Username, ConfirmationCode, Password } = params;
301
+ const userPool = this.findUserPoolByClientId(ClientId);
302
+ if (!userPool) throw new Error(`Client ${ClientId} not found`);
303
+
223
304
  const user = this.findUserByUsername(Username, ClientId);
224
305
  if (!user) throw new Error(`User not found: ${Username}`);
306
+
307
+ if (user.UserStatus !== "CONFIRMED") {
308
+ const err = new Error("Cannot reset password for the user as there is no registered/verified email or phone_number");
309
+ err.code = "InvalidParameterException";
310
+ throw err;
311
+ }
312
+
313
+ if (user.PasswordResetCode && user.PasswordResetCode !== ConfirmationCode) {
314
+ const err = new Error("Invalid verification code provided, please try again.");
315
+ err.code = "CodeMismatchException";
316
+ throw err;
317
+ }
318
+
225
319
  user.Password = this.hashPassword(Password);
226
- user.UserStatus = 'CONFIRMED';
320
+ delete user.PasswordResetCode;
227
321
  this.persistUsers();
228
322
  return {};
229
323
  }
@@ -231,16 +325,215 @@ class CognitoSimulator {
231
325
  changePassword(params = {}) {
232
326
  const { AccessToken, PreviousPassword, ProposedPassword } = params;
233
327
  const session = this.accessTokens.get(AccessToken);
234
- if (!session) throw new Error('Invalid access token');
328
+ if (!session) throw new Error("Invalid access token");
235
329
  const user = this.users.get(session.UserId);
236
- if (!user) throw new Error('User not found');
237
- if (!this.verifyPassword(PreviousPassword, user.Password)) throw new Error('Incorrect previous password');
330
+ if (!user) throw new Error("User not found");
331
+ if (!this.verifyPassword(PreviousPassword, user.Password)) throw new Error("Incorrect previous password");
238
332
  user.Password = this.hashPassword(ProposedPassword);
239
333
  this.persistUsers();
240
334
  return {};
241
335
  }
242
336
 
243
- respondToAuthChallenge(params = {}) {
337
+ async respondToAuthChallenge(params = {}) {
338
+ const { ChallengeName } = params;
339
+
340
+ // NEW_PASSWORD_REQUIRED — user was created by admin and must set a permanent password
341
+ if (ChallengeName === "NEW_PASSWORD_REQUIRED") {
342
+ const session = this.customAuthSessions.get(params.Session);
343
+ if (!session || session.challenge !== "NEW_PASSWORD_REQUIRED") {
344
+ throw new Error("Invalid session token");
345
+ }
346
+
347
+ const newPassword = params.ChallengeResponses?.NEW_PASSWORD;
348
+ if (!newPassword) throw new Error("NEW_PASSWORD is required");
349
+
350
+ const user = this.users.get(session.userId);
351
+ const userPool = this.userPools.get(session.userPoolId);
352
+ if (!user || !userPool) throw new Error("Invalid session");
353
+
354
+ // 1. PreAuthentication — dispara antes de processar a nova senha
355
+ const preAuthEvent = this._buildTriggerEvent("PreAuthentication_Authentication", userPool, user, session.clientId, {
356
+ userAttributes: this._triggerUserAttributes(user),
357
+ validationData: {},
358
+ });
359
+ await this._invokeTrigger(userPool, "PreAuthentication", preAuthEvent);
360
+
361
+ // 2. Aplica nova senha e confirma usuário
362
+ user.Password = this.hashPassword(newPassword);
363
+ user.UserStatus = "CONFIRMED";
364
+ user.LastModifiedDate = new Date().toISOString();
365
+ this.persistUsers();
366
+ this.customAuthSessions.delete(params.Session);
367
+
368
+ logger.debug(`🔑 Senha alterada e usuário confirmado: ${user.Username}`);
369
+
370
+ // 3. PostConfirmation — dispara após confirmação do usuário (troca de senha forçada)
371
+ const postConfirmEvent = this._buildTriggerEvent("PostConfirmation_ConfirmSignUp", userPool, user, session.clientId, {
372
+ userAttributes: this._triggerUserAttributes(user),
373
+ });
374
+ try {
375
+ await this._invokeTrigger(userPool, "PostConfirmation", postConfirmEvent);
376
+ } catch (err) {
377
+ logger.error(`PostConfirmation trigger error (ignored): ${err.message}`);
378
+ }
379
+
380
+ // 4. PreTokenGeneration — antes de gerar tokens
381
+ const preTokenEvent = this._buildTriggerEvent("TokenGeneration_Authentication", userPool, user, session.clientId, {
382
+ userAttributes: this._triggerUserAttributes(user),
383
+ groupConfiguration: { groupsToOverride: [], iamRolesToOverride: [], preferredRole: null },
384
+ });
385
+ const preTokenResponse = await this._invokeTrigger(userPool, "PreTokenGeneration", preTokenEvent);
386
+ const claimsOverride = preTokenResponse?.response?.claimsOverrideDetails || null;
387
+
388
+ // 5. Gera tokens
389
+ const accessToken = this.generateAccessToken(user, userPool, session.clientId);
390
+ const idToken = this.generateIdToken(user, userPool, session.clientId, claimsOverride);
391
+ const refreshToken = this.generateRefreshToken(user, userPool, session.clientId);
392
+
393
+ const sessionId = uuidv4();
394
+ const authSession = {
395
+ Id: sessionId,
396
+ UserId: user.UserId,
397
+ UserPoolId: userPool.Id,
398
+ ClientId: session.clientId,
399
+ AccessToken: accessToken,
400
+ IdToken: idToken,
401
+ RefreshToken: refreshToken,
402
+ CreatedAt: new Date().toISOString(),
403
+ ExpiresAt: new Date(Date.now() + 3600000).toISOString(),
404
+ };
405
+ this.sessions.set(sessionId, authSession);
406
+ this.accessTokens.set(accessToken, authSession);
407
+ this.refreshTokens.set(refreshToken, authSession);
408
+ this.persistSessions();
409
+
410
+ // 6. PostAuthentication — após auth bem-sucedida (non-blocking)
411
+ const postAuthEvent = this._buildTriggerEvent("PostAuthentication_Authentication", userPool, user, session.clientId, {
412
+ userAttributes: this._triggerUserAttributes(user),
413
+ newDeviceUsed: false,
414
+ });
415
+ try {
416
+ await this._invokeTrigger(userPool, "PostAuthentication", postAuthEvent);
417
+ } catch (err) {
418
+ logger.error(`PostAuthentication trigger error (ignored): ${err.message}`);
419
+ }
420
+
421
+ return {
422
+ AuthenticationResult: {
423
+ AccessToken: accessToken,
424
+ IdToken: idToken,
425
+ RefreshToken: refreshToken,
426
+ TokenType: "Bearer",
427
+ ExpiresIn: 3600,
428
+ },
429
+ ChallengeName: null,
430
+ };
431
+ }
432
+
433
+ if (ChallengeName === "CUSTOM_CHALLENGE") {
434
+ const session = this.customAuthSessions.get(params.Session);
435
+ if (!session) {
436
+ throw new Error("Invalid session token");
437
+ }
438
+
439
+ const user = this.users.get(session.userId);
440
+ const userPool = this.userPools.get(session.userPoolId);
441
+
442
+ // Invoke VerifyAuthChallengeResponse
443
+ const verifyEvent = this._buildTriggerEvent("VerifyAuthChallengeResponse_Authentication", userPool, user, session.clientId, {
444
+ challengeAnswer: params.ChallengeResponses?.ANSWER,
445
+ privateChallengeParameters: session.privateChallengeParameters,
446
+ });
447
+ const verifyResponse = await this._invokeTrigger(userPool, "VerifyAuthChallengeResponse", verifyEvent);
448
+
449
+ // Append challenge result to session
450
+ session.session.push({
451
+ challengeName: "CUSTOM_CHALLENGE",
452
+ challengeResult: verifyResponse?.response?.answerCorrect === true,
453
+ challengeMetadata: "",
454
+ });
455
+
456
+ // Invoke DefineAuthChallenge again with updated session
457
+ const defineEvent = this._buildTriggerEvent("DefineAuthChallenge_Authentication", userPool, user, session.clientId, {
458
+ session: session.session,
459
+ });
460
+ const defineResponse = await this._invokeTrigger(userPool, "DefineAuthChallenge", defineEvent);
461
+
462
+ if (defineResponse?.response?.issueTokens === true) {
463
+ // 1. PreTokenGeneration antes de gerar tokens
464
+ const preTokenEvent = this._buildTriggerEvent("TokenGeneration_Authentication", userPool, user, session.clientId, {
465
+ userAttributes: this._triggerUserAttributes(user),
466
+ groupConfiguration: { groupsToOverride: [], iamRolesToOverride: [], preferredRole: null },
467
+ });
468
+ const preTokenResponse = await this._invokeTrigger(userPool, "PreTokenGeneration", preTokenEvent);
469
+ const claimsOverride = preTokenResponse?.response?.claimsOverrideDetails || null;
470
+
471
+ // 2. Gera tokens
472
+ const accessToken = this.generateAccessToken(user, userPool, session.clientId);
473
+ const idToken = this.generateIdToken(user, userPool, session.clientId, claimsOverride);
474
+ const refreshToken = this.generateRefreshToken(user, userPool, session.clientId);
475
+
476
+ const sessionId = uuidv4();
477
+ const authSession = {
478
+ Id: sessionId,
479
+ UserId: user.UserId,
480
+ UserPoolId: userPool.Id,
481
+ ClientId: session.clientId,
482
+ AccessToken: accessToken,
483
+ IdToken: idToken,
484
+ RefreshToken: refreshToken,
485
+ CreatedAt: new Date().toISOString(),
486
+ ExpiresAt: new Date(Date.now() + 3600000).toISOString(),
487
+ };
488
+
489
+ this.sessions.set(sessionId, authSession);
490
+ this.accessTokens.set(accessToken, authSession);
491
+ this.refreshTokens.set(refreshToken, authSession);
492
+ this.persistSessions();
493
+
494
+ this.customAuthSessions.delete(params.Session);
495
+
496
+ // 3. PostAuthentication (non-blocking)
497
+ const postAuthEvent = this._buildTriggerEvent("PostAuthentication_Authentication", userPool, user, session.clientId, {
498
+ userAttributes: this._triggerUserAttributes(user),
499
+ newDeviceUsed: false,
500
+ });
501
+ try {
502
+ await this._invokeTrigger(userPool, "PostAuthentication", postAuthEvent);
503
+ } catch (err) {
504
+ logger.error(`PostAuthentication trigger error (ignored): ${err.message}`);
505
+ }
506
+
507
+ return {
508
+ AuthenticationResult: {
509
+ AccessToken: accessToken,
510
+ IdToken: idToken,
511
+ RefreshToken: refreshToken,
512
+ TokenType: "Bearer",
513
+ ExpiresIn: 3600,
514
+ },
515
+ ChallengeName: null,
516
+ };
517
+ }
518
+
519
+ // Issue next challenge
520
+ const createEvent = this._buildTriggerEvent("CreateAuthChallenge_Authentication", userPool, user, session.clientId, {
521
+ challengeName: "CUSTOM_CHALLENGE",
522
+ session: session.session,
523
+ });
524
+ const createResponse = await this._invokeTrigger(userPool, "CreateAuthChallenge", createEvent);
525
+
526
+ // Update stored private challenge parameters for next round
527
+ session.privateChallengeParameters = createResponse?.response?.privateChallengeParameters || {};
528
+
529
+ return {
530
+ ChallengeName: "CUSTOM_CHALLENGE",
531
+ ChallengeParameters: createResponse?.response?.publicChallengeParameters || {},
532
+ Session: params.Session,
533
+ AuthenticationResult: null,
534
+ };
535
+ }
536
+
244
537
  return { AuthenticationResult: null, ChallengeName: null };
245
538
  }
246
539
 
@@ -272,24 +565,24 @@ class CognitoSimulator {
272
565
  getUser(params = {}) {
273
566
  const { AccessToken } = params;
274
567
  const session = this.accessTokens.get(AccessToken);
275
- if (!session) throw new Error('Invalid access token');
568
+ if (!session) throw new Error("Invalid access token");
276
569
  // Check session is still active
277
- if (!this.sessions.has(session.Id)) throw new Error('Token has been revoked');
570
+ if (!this.sessions.has(session.Id)) throw new Error("Token has been revoked");
278
571
  const user = this.users.get(session.UserId);
279
- if (!user) throw new Error('User not found');
572
+ if (!user) throw new Error("User not found");
280
573
  return {
281
574
  Username: user.Username,
282
575
  UserAttributes: this.formatUserAttributes(user.Attributes),
283
- UserStatus: user.UserStatus
576
+ UserStatus: user.UserStatus,
284
577
  };
285
578
  }
286
579
 
287
580
  updateUserAttributes(params = {}) {
288
581
  const { AccessToken, UserAttributes } = params;
289
582
  const session = this.accessTokens.get(AccessToken);
290
- if (!session) throw new Error('Invalid access token');
583
+ if (!session) throw new Error("Invalid access token");
291
584
  const user = this.users.get(session.UserId);
292
- if (!user) throw new Error('User not found');
585
+ if (!user) throw new Error("User not found");
293
586
  const updates = this.normalizeUserAttributes(UserAttributes || []);
294
587
  Object.assign(user.Attributes, updates);
295
588
  this.persistUsers();
@@ -299,7 +592,7 @@ class CognitoSimulator {
299
592
  deleteUser(params = {}) {
300
593
  const { AccessToken } = params;
301
594
  const session = this.accessTokens.get(AccessToken);
302
- if (!session) throw new Error('Invalid access token');
595
+ if (!session) throw new Error("Invalid access token");
303
596
  this.users.delete(session.UserId);
304
597
  this.persistUsers();
305
598
  return {};
@@ -327,7 +620,7 @@ class CognitoSimulator {
327
620
  const { UserPoolId, Username } = params;
328
621
  const user = this.findUserByUsername(Username, null, UserPoolId);
329
622
  if (!user) throw new Error(`User not found: ${Username}`);
330
- user.UserStatus = 'RESET_REQUIRED';
623
+ user.UserStatus = "RESET_REQUIRED";
331
624
  this.persistUsers();
332
625
  return {};
333
626
  }
@@ -354,14 +647,14 @@ class CognitoSimulator {
354
647
 
355
648
  deleteUserPool(params) {
356
649
  const { UserPoolId } = params;
357
-
650
+
358
651
  if (!this.userPools.has(UserPoolId)) {
359
652
  throw new Error(`User pool ${UserPoolId} not found`);
360
653
  }
361
-
654
+
362
655
  this.userPools.delete(UserPoolId);
363
656
  this.persistUserPools();
364
-
657
+
365
658
  return {};
366
659
  }
367
660
 
@@ -369,15 +662,15 @@ class CognitoSimulator {
369
662
 
370
663
  createUserPoolClient(params) {
371
664
  const { UserPoolId, ClientName, GenerateSecret, RefreshTokenValidity, AccessTokenValidity, IdTokenValidity, AllowedOAuthFlows, AllowedOAuthScopes, CallbackURLs, LogoutURLs } = params;
372
-
665
+
373
666
  const userPool = this.userPools.get(UserPoolId);
374
667
  if (!userPool) {
375
668
  throw new Error(`User pool ${UserPoolId} not found`);
376
669
  }
377
-
378
- const clientId = crypto.randomBytes(20).toString('hex');
379
- const clientSecret = GenerateSecret ? crypto.randomBytes(32).toString('hex') : null;
380
-
670
+
671
+ const clientId = crypto.randomBytes(20).toString("hex");
672
+ const clientSecret = GenerateSecret ? crypto.randomBytes(32).toString("hex") : null;
673
+
381
674
  const client = {
382
675
  ClientId: clientId,
383
676
  ClientName: ClientName,
@@ -386,19 +679,19 @@ class CognitoSimulator {
386
679
  RefreshTokenValidity: RefreshTokenValidity || 30,
387
680
  AccessTokenValidity: AccessTokenValidity || 1,
388
681
  IdTokenValidity: IdTokenValidity || 1,
389
- AllowedOAuthFlows: AllowedOAuthFlows || ['code'],
390
- AllowedOAuthScopes: AllowedOAuthScopes || ['openid', 'email', 'profile'],
682
+ AllowedOAuthFlows: AllowedOAuthFlows || ["code"],
683
+ AllowedOAuthScopes: AllowedOAuthScopes || ["openid", "email", "profile"],
391
684
  CallbackURLs: CallbackURLs || [],
392
685
  LogoutURLs: LogoutURLs || [],
393
686
  CreatedDate: new Date().toISOString(),
394
- LastModifiedDate: new Date().toISOString()
687
+ LastModifiedDate: new Date().toISOString(),
395
688
  };
396
-
689
+
397
690
  userPool.Clients.set(clientId, client);
398
691
  this.persistUserPools();
399
-
692
+
400
693
  logger.debug(`✅ User Pool Client criado: ${ClientName} (${clientId})`);
401
-
694
+
402
695
  return {
403
696
  UserPoolClient: {
404
697
  ClientId: client.ClientId,
@@ -412,106 +705,266 @@ class CognitoSimulator {
412
705
  AllowedOAuthScopes: client.AllowedOAuthScopes,
413
706
  CallbackURLs: client.CallbackURLs,
414
707
  LogoutURLs: client.LogoutURLs,
415
- CreationDate: client.CreatedDate
416
- }
708
+ CreationDate: client.CreatedDate,
709
+ },
417
710
  };
418
711
  }
419
712
 
420
713
  // ============ User Operations ============
421
714
 
422
- signUp(params = {}) {
715
+ async signUp(params = {}) {
423
716
  const { ClientId, Username, Password, UserAttributes, ValidationData } = params;
424
-
717
+
425
718
  // Encontra o user pool pelo client id
426
719
  const userPool = this.findUserPoolByClientId(ClientId);
427
720
  if (!userPool) {
428
721
  throw new Error(`Client ${ClientId} not found`);
429
722
  }
430
-
723
+
431
724
  // Verifica se usuário já existe
432
- const existingUser = Array.from(this.users.values()).find(
433
- u => u.Username === Username && u.UserPoolId === userPool.Id
434
- );
435
-
725
+ const existingUser = Array.from(this.users.values()).find((u) => u.Username === Username && u.UserPoolId === userPool.Id);
726
+
436
727
  if (existingUser) {
437
728
  throw new Error(`User already exists: ${Username}`);
438
729
  }
439
-
730
+
440
731
  const userId = uuidv4();
732
+ const confirmationCode = Math.floor(100000 + Math.random() * 900000).toString();
733
+
441
734
  const user = {
442
735
  Username: Username,
443
736
  UserPoolId: userPool.Id,
444
737
  UserId: userId,
445
738
  Attributes: this.normalizeUserAttributes(UserAttributes || []),
446
739
  Enabled: true,
447
- UserStatus: 'CONFIRMED', // Por padrão, confirma imediatamente (para teste)
740
+ UserStatus: "UNCONFIRMED",
448
741
  CreatedDate: new Date().toISOString(),
449
742
  LastModifiedDate: new Date().toISOString(),
450
743
  Password: this.hashPassword(Password),
744
+ ConfirmationCode: confirmationCode,
451
745
  MfaOptions: [],
452
746
  PreferredMfaSetting: null,
453
- UserMFASettingList: []
747
+ UserMFASettingList: [],
454
748
  };
455
-
749
+
750
+ // Invoke PreSignUp trigger before persisting the user
751
+ const event = this._buildTriggerEvent("PreSignUp_SignUp", userPool, user, ClientId, {
752
+ userAttributes: this.normalizeUserAttributes(UserAttributes || []),
753
+ validationData: ValidationData || {},
754
+ clientMetadata: {},
755
+ });
756
+ const triggerResponse = await this._invokeTrigger(userPool, "PreSignUp", event);
757
+
758
+ if (triggerResponse !== null) {
759
+ if (triggerResponse.response?.autoConfirmUser === true) {
760
+ user.UserStatus = "CONFIRMED";
761
+ delete user.ConfirmationCode;
762
+ }
763
+ if (triggerResponse.response?.autoVerifyEmail === true) {
764
+ user.Attributes.email_verified = "true";
765
+ }
766
+ }
767
+
456
768
  this.users.set(userId, user);
457
769
  userPool.Users.push(userId);
458
770
  userPool.EstimatedNumberOfUsers++;
459
771
  this.persistUsers();
460
772
  this.persistUserPools();
461
-
462
- logger.debug(`✅ Usuário criado: ${Username} (${userId})`);
463
-
773
+
774
+ const userEmail = user.Attributes.email || Username;
775
+
776
+ if (user.UserStatus === "UNCONFIRMED") {
777
+ logger.info(`📧 [COGNITO] Código de confirmação para "${Username}": ${confirmationCode}`);
778
+ }
779
+
780
+ logger.debug(`✅ Usuário criado: ${Username} (${userId}) — status: ${user.UserStatus}`);
781
+
782
+ // PostConfirmation — dispara se usuário foi auto-confirmado pelo PreSignUp
783
+ if (user.UserStatus === "CONFIRMED") {
784
+ const postConfirmEvent = this._buildTriggerEvent("PostConfirmation_ConfirmSignUp", userPool, user, ClientId, {
785
+ userAttributes: this._triggerUserAttributes(user),
786
+ });
787
+ try {
788
+ await this._invokeTrigger(userPool, "PostConfirmation", postConfirmEvent);
789
+ } catch (err) {
790
+ logger.error(`PostConfirmation trigger error (ignored): ${err.message}`);
791
+ }
792
+ }
793
+
464
794
  return {
465
- UserConfirmed: true,
795
+ UserConfirmed: user.UserStatus === "CONFIRMED",
466
796
  UserSub: userId,
467
- CodeDeliveryDetails: null
797
+ CodeDeliveryDetails: user.UserStatus === "UNCONFIRMED"
798
+ ? { Destination: userEmail, DeliveryMedium: "EMAIL", AttributeName: "email" }
799
+ : null,
468
800
  };
469
801
  }
470
802
 
471
- confirmSignUp(params) {
803
+ async confirmSignUp(params) {
472
804
  const { ClientId, Username, ConfirmationCode } = params;
473
-
805
+
806
+ const userPool = this.findUserPoolByClientId(ClientId);
807
+ if (!userPool) {
808
+ throw new Error(`Client ${ClientId} not found`);
809
+ }
810
+
474
811
  const user = this.findUserByUsername(Username, ClientId);
475
812
  if (!user) {
476
813
  throw new Error(`User not found: ${Username}`);
477
814
  }
478
-
479
- user.UserStatus = 'CONFIRMED';
815
+
816
+ if (user.ConfirmationCode && user.ConfirmationCode !== ConfirmationCode) {
817
+ throw new Error("Invalid verification code provided, please try again.");
818
+ }
819
+
820
+ user.UserStatus = "CONFIRMED";
480
821
  user.LastModifiedDate = new Date().toISOString();
822
+ delete user.ConfirmationCode;
481
823
  this.persistUsers();
482
-
824
+
825
+ const event = this._buildTriggerEvent("PostConfirmation_ConfirmSignUp", userPool, user, ClientId, {
826
+ userAttributes: this._triggerUserAttributes(user),
827
+ });
828
+ try {
829
+ await this._invokeTrigger(userPool, "PostConfirmation", event);
830
+ } catch (err) {
831
+ logger.error(`PostConfirmation trigger error (ignored): ${err.message}`);
832
+ }
833
+
483
834
  return {};
484
835
  }
485
836
 
486
- initiateAuth(params) {
837
+ async initiateAuth(params) {
487
838
  const { AuthFlow, ClientId, AuthParameters } = params;
488
839
  const userPool = this.findUserPoolByClientId(ClientId);
489
-
840
+
490
841
  if (!userPool) {
491
842
  throw new Error(`Client ${ClientId} not found`);
492
843
  }
493
-
844
+
845
+ // CUSTOM_AUTH flow — no password check, challenge-based
846
+ if (AuthFlow === "CUSTOM_AUTH") {
847
+ const username = AuthParameters.USERNAME;
848
+ const user = this.findUserByUsername(username, ClientId);
849
+ if (!user) {
850
+ throw new Error(`User not found: ${username}`);
851
+ }
852
+
853
+ // 1. PreAuthentication — dispara antes de qualquer challenge
854
+ const preAuthEvent = this._buildTriggerEvent("PreAuthentication_Authentication", userPool, user, ClientId, {
855
+ userAttributes: this._triggerUserAttributes(user),
856
+ validationData: AuthParameters.ValidationData || {},
857
+ });
858
+ await this._invokeTrigger(userPool, "PreAuthentication", preAuthEvent);
859
+
860
+ // 2. DefineAuthChallenge — define qual challenge usar
861
+ const defineEvent = this._buildTriggerEvent("DefineAuthChallenge_Authentication", userPool, user, ClientId, {
862
+ session: [],
863
+ });
864
+ const defineResponse = await this._invokeTrigger(userPool, "DefineAuthChallenge", defineEvent);
865
+
866
+ if (defineResponse?.response?.challengeName === "CUSTOM_CHALLENGE") {
867
+ const createEvent = this._buildTriggerEvent("CreateAuthChallenge_Authentication", userPool, user, ClientId, {
868
+ challengeName: "CUSTOM_CHALLENGE",
869
+ session: [],
870
+ });
871
+ const createResponse = await this._invokeTrigger(userPool, "CreateAuthChallenge", createEvent);
872
+
873
+ const sessionToken = uuidv4();
874
+ this.customAuthSessions.set(sessionToken, {
875
+ sessionToken,
876
+ userId: user.UserId,
877
+ userPoolId: userPool.Id,
878
+ clientId: ClientId,
879
+ session: [],
880
+ privateChallengeParameters: createResponse?.response?.privateChallengeParameters || {},
881
+ });
882
+
883
+ return {
884
+ ChallengeName: "CUSTOM_CHALLENGE",
885
+ ChallengeParameters: createResponse?.response?.publicChallengeParameters || {},
886
+ Session: sessionToken,
887
+ AuthenticationResult: null,
888
+ };
889
+ }
890
+
891
+ // DefineAuthChallenge did not return CUSTOM_CHALLENGE — nothing to do
892
+ return {
893
+ ChallengeName: null,
894
+ ChallengeParameters: {},
895
+ Session: null,
896
+ AuthenticationResult: null,
897
+ };
898
+ }
899
+
494
900
  const username = AuthParameters.USERNAME;
495
901
  const password = AuthParameters.PASSWORD;
496
-
902
+
497
903
  const user = this.findUserByUsername(username, ClientId);
498
904
  if (!user) {
499
905
  throw new Error(`User not found: ${username}`);
500
906
  }
501
-
502
- if (user.UserStatus !== 'CONFIRMED') {
503
- throw new Error(`User not confirmed: ${username}`);
907
+
908
+ // FORCE_CHANGE_PASSWORD validate temp password then return NEW_PASSWORD_REQUIRED challenge
909
+ if (user.UserStatus === "FORCE_CHANGE_PASSWORD") {
910
+ if (!this.verifyPassword(password, user.Password)) {
911
+ throw new Error("Incorrect username or password");
912
+ }
913
+ const sessionToken = uuidv4();
914
+ this.customAuthSessions.set(sessionToken, {
915
+ sessionToken,
916
+ userId: user.UserId,
917
+ userPoolId: userPool.Id,
918
+ clientId: ClientId,
919
+ challenge: "NEW_PASSWORD_REQUIRED",
920
+ });
921
+ return {
922
+ ChallengeName: "NEW_PASSWORD_REQUIRED",
923
+ ChallengeParameters: {
924
+ USER_ID_FOR_SRP: user.Username,
925
+ requiredAttributes: "[]",
926
+ userAttributes: JSON.stringify(this._triggerUserAttributes(user)),
927
+ },
928
+ Session: sessionToken,
929
+ AuthenticationResult: null,
930
+ };
931
+ }
932
+
933
+ if (user.UserStatus !== "CONFIRMED") {
934
+ // Valida senha antes de revelar o status — igual ao Cognito real
935
+ if (!this.verifyPassword(password, user.Password)) {
936
+ throw new Error("Incorrect username or password");
937
+ }
938
+ const err = new Error("User is not confirmed.");
939
+ err.code = "UserNotConfirmedException";
940
+ throw err;
504
941
  }
505
-
942
+
943
+ // 1. PreAuthentication — dispara antes de validar senha
944
+ const preAuthEvent = this._buildTriggerEvent("PreAuthentication_Authentication", userPool, user, ClientId, {
945
+ userAttributes: this._triggerUserAttributes(user),
946
+ validationData: AuthParameters.ValidationData || {},
947
+ });
948
+ await this._invokeTrigger(userPool, "PreAuthentication", preAuthEvent);
949
+
950
+ // 2. Valida senha
506
951
  if (!this.verifyPassword(password, user.Password)) {
507
- throw new Error('Incorrect username or password');
952
+ throw new Error("Incorrect username or password");
508
953
  }
509
-
510
- // Gera tokens JWT
954
+
955
+ // 3. PreTokenGeneration — dispara antes de gerar tokens, pode sobrescrever claims
956
+ const preTokenEvent = this._buildTriggerEvent("TokenGeneration_Authentication", userPool, user, ClientId, {
957
+ userAttributes: this._triggerUserAttributes(user),
958
+ groupConfiguration: { groupsToOverride: [], iamRolesToOverride: [], preferredRole: null },
959
+ });
960
+ const preTokenResponse = await this._invokeTrigger(userPool, "PreTokenGeneration", preTokenEvent);
961
+ const claimsOverride = preTokenResponse?.response?.claimsOverrideDetails || null;
962
+
963
+ // 4. Gera tokens com claims override se houver
511
964
  const accessToken = this.generateAccessToken(user, userPool, ClientId);
512
- const idToken = this.generateIdToken(user, userPool, ClientId);
965
+ const idToken = this.generateIdToken(user, userPool, ClientId, claimsOverride);
513
966
  const refreshToken = this.generateRefreshToken(user, userPool, ClientId);
514
-
967
+
515
968
  const sessionId = uuidv4();
516
969
  const session = {
517
970
  Id: sessionId,
@@ -522,69 +975,85 @@ class CognitoSimulator {
522
975
  IdToken: idToken,
523
976
  RefreshToken: refreshToken,
524
977
  CreatedAt: new Date().toISOString(),
525
- ExpiresAt: new Date(Date.now() + 3600000).toISOString() // 1 hora
978
+ ExpiresAt: new Date(Date.now() + 3600000).toISOString(),
526
979
  };
527
-
980
+
528
981
  this.sessions.set(sessionId, session);
529
982
  this.accessTokens.set(accessToken, session);
530
983
  this.refreshTokens.set(refreshToken, session);
531
984
  this.persistSessions();
532
-
985
+
986
+ // 5. PostAuthentication — dispara após auth bem-sucedida (não bloqueia)
987
+ const postAuthEvent = this._buildTriggerEvent("PostAuthentication_Authentication", userPool, user, ClientId, {
988
+ userAttributes: this._triggerUserAttributes(user),
989
+ newDeviceUsed: false,
990
+ });
991
+ try {
992
+ await this._invokeTrigger(userPool, "PostAuthentication", postAuthEvent);
993
+ } catch (err) {
994
+ logger.error(`PostAuthentication trigger error (ignored): ${err.message}`);
995
+ }
996
+
533
997
  logger.debug(`🔐 Usuário autenticado: ${username}`);
534
- this.audit.record({ eventName: 'InitiateAuth', readOnly: false, resources: [{ ARN: userPool.Arn, type: 'AWS::Cognito::UserPool' }], requestParameters: { clientId: ClientId, authFlow: AuthFlow } });
535
-
998
+ this.audit.record({
999
+ eventName: "InitiateAuth",
1000
+ readOnly: false,
1001
+ resources: [{ ARN: userPool.Arn, type: "AWS::Cognito::UserPool" }],
1002
+ requestParameters: { clientId: ClientId, authFlow: AuthFlow },
1003
+ });
1004
+
536
1005
  return {
537
1006
  AuthenticationResult: {
538
1007
  AccessToken: accessToken,
539
1008
  IdToken: idToken,
540
1009
  RefreshToken: refreshToken,
541
- TokenType: 'Bearer',
542
- ExpiresIn: 3600
1010
+ TokenType: "Bearer",
1011
+ ExpiresIn: 3600,
543
1012
  },
544
1013
  ChallengeName: null,
545
- Session: null
1014
+ Session: null,
546
1015
  };
547
1016
  }
548
1017
 
549
1018
  getToken(params) {
550
1019
  const { AuthFlow, ClientId, AuthParameters } = params;
551
-
552
- if (AuthFlow === 'REFRESH_TOKEN_AUTH') {
1020
+
1021
+ if (AuthFlow === "REFRESH_TOKEN_AUTH") {
553
1022
  const refreshToken = AuthParameters.REFRESH_TOKEN;
554
1023
  const session = this.refreshTokens.get(refreshToken);
555
-
1024
+
556
1025
  if (!session) {
557
- throw new Error('Invalid refresh token');
1026
+ throw new Error("Invalid refresh token");
558
1027
  }
559
-
1028
+
560
1029
  const user = this.users.get(session.UserId);
561
1030
  const userPool = this.userPools.get(session.UserPoolId);
562
-
1031
+
563
1032
  if (!user || !userPool) {
564
- throw new Error('Invalid session');
1033
+ throw new Error("Invalid session");
565
1034
  }
566
-
1035
+
567
1036
  // Gera novos tokens
568
1037
  const newAccessToken = this.generateAccessToken(user, userPool, session.ClientId);
569
1038
  const newIdToken = this.generateIdToken(user, userPool, session.ClientId);
570
-
1039
+
571
1040
  session.AccessToken = newAccessToken;
572
1041
  session.IdToken = newIdToken;
573
1042
  session.ExpiresAt = new Date(Date.now() + 3600000).toISOString();
574
-
1043
+
575
1044
  this.accessTokens.set(newAccessToken, session);
576
1045
  this.persistSessions();
577
-
1046
+
578
1047
  return {
579
1048
  AuthenticationResult: {
580
1049
  AccessToken: newAccessToken,
581
1050
  IdToken: newIdToken,
582
- TokenType: 'Bearer',
583
- ExpiresIn: 3600
584
- }
1051
+ TokenType: "Bearer",
1052
+ ExpiresIn: 3600,
1053
+ },
585
1054
  };
586
1055
  }
587
-
1056
+
588
1057
  throw new Error(`Unsupported AuthFlow: ${AuthFlow}`);
589
1058
  }
590
1059
 
@@ -593,64 +1062,76 @@ class CognitoSimulator {
593
1062
  generateAccessToken(user, userPool, clientId) {
594
1063
  const payload = {
595
1064
  sub: user.UserId,
596
- token_use: 'access',
1065
+ token_use: "access",
597
1066
  client_id: clientId,
598
1067
  username: user.Username,
599
- scope: 'aws.cognito.signin.user.admin',
1068
+ scope: "aws.cognito.signin.user.admin",
600
1069
  iss: `https://cognito-idp.local/${userPool.Id}`,
601
1070
  exp: Math.floor(Date.now() / 1000) + 3600,
602
- iat: Math.floor(Date.now() / 1000)
1071
+ iat: Math.floor(Date.now() / 1000),
603
1072
  };
604
-
605
- return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
1073
+
1074
+ return jwt.sign(payload, this.jwtSecret, { algorithm: "HS256" });
606
1075
  }
607
1076
 
608
- generateIdToken(user, userPool, clientId) {
1077
+ generateIdToken(user, userPool, clientId, claimsOverride = null) {
609
1078
  const payload = {
610
1079
  sub: user.UserId,
611
- token_use: 'id',
1080
+ token_use: "id",
612
1081
  client_id: clientId,
613
1082
  email: user.Attributes.email,
614
1083
  email_verified: user.Attributes.email_verified || true,
615
1084
  username: user.Username,
616
1085
  iss: `https://cognito-idp.local/${userPool.Id}`,
617
1086
  exp: Math.floor(Date.now() / 1000) + 3600,
618
- iat: Math.floor(Date.now() / 1000)
1087
+ iat: Math.floor(Date.now() / 1000),
619
1088
  };
620
-
1089
+
621
1090
  // Adiciona outros atributos do usuário
622
1091
  for (const [key, value] of Object.entries(user.Attributes)) {
623
- if (key !== 'email' && key !== 'email_verified') {
1092
+ if (key !== "email" && key !== "email_verified") {
624
1093
  payload[key] = value;
625
1094
  }
626
1095
  }
627
-
628
- return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
1096
+
1097
+ // Apply PreTokenGeneration claims override
1098
+ if (claimsOverride !== null) {
1099
+ if (claimsOverride.claimsToAddOrOverride) {
1100
+ Object.assign(payload, claimsOverride.claimsToAddOrOverride);
1101
+ }
1102
+ if (Array.isArray(claimsOverride.claimsToSuppress)) {
1103
+ for (const key of claimsOverride.claimsToSuppress) {
1104
+ delete payload[key];
1105
+ }
1106
+ }
1107
+ }
1108
+
1109
+ return jwt.sign(payload, this.jwtSecret, { algorithm: "HS256" });
629
1110
  }
630
1111
 
631
1112
  generateRefreshToken(user, userPool, clientId) {
632
1113
  const payload = {
633
1114
  sub: user.UserId,
634
- token_use: 'refresh',
1115
+ token_use: "refresh",
635
1116
  client_id: clientId,
636
1117
  username: user.Username,
637
1118
  iss: `https://cognito-idp.local/${userPool.Id}`,
638
1119
  exp: Math.floor(Date.now() / 1000) + 2592000, // 30 dias
639
- iat: Math.floor(Date.now() / 1000)
1120
+ iat: Math.floor(Date.now() / 1000),
640
1121
  };
641
-
642
- return jwt.sign(payload, this.jwtSecret, { algorithm: 'HS256' });
1122
+
1123
+ return jwt.sign(payload, this.jwtSecret, { algorithm: "HS256" });
643
1124
  }
644
1125
 
645
1126
  verifyAccessToken(token) {
646
1127
  try {
647
1128
  const decoded = jwt.verify(token, this.jwtSecret);
648
1129
  const session = this.accessTokens.get(token);
649
-
1130
+
650
1131
  if (!session || session.ExpiresAt < new Date().toISOString()) {
651
1132
  return null;
652
1133
  }
653
-
1134
+
654
1135
  return decoded;
655
1136
  } catch (error) {
656
1137
  return null;
@@ -662,16 +1143,16 @@ class CognitoSimulator {
662
1143
  adminGetUser(params) {
663
1144
  const { UserPoolId, Username } = params;
664
1145
  const userPool = this.userPools.get(UserPoolId);
665
-
1146
+
666
1147
  if (!userPool) {
667
1148
  throw new Error(`User pool ${UserPoolId} not found`);
668
1149
  }
669
-
1150
+
670
1151
  const user = this.findUserByUsername(Username, null, UserPoolId);
671
1152
  if (!user) {
672
1153
  throw new Error(`User not found: ${Username}`);
673
1154
  }
674
-
1155
+
675
1156
  return {
676
1157
  Username: user.Username,
677
1158
  UserAttributes: this.formatUserAttributes(user.Attributes),
@@ -681,18 +1162,20 @@ class CognitoSimulator {
681
1162
  UserStatus: user.UserStatus,
682
1163
  MFAOptions: user.MfaOptions,
683
1164
  PreferredMfaSetting: user.PreferredMfaSetting,
684
- UserMFASettingList: user.UserMFASettingList
1165
+ UserMFASettingList: user.UserMFASettingList,
685
1166
  };
686
1167
  }
687
1168
 
688
1169
  adminCreateUser(params) {
689
1170
  const { UserPoolId, Username, UserAttributes, TemporaryPassword, DesiredDeliveryMediums } = params;
690
1171
  const userPool = this.userPools.get(UserPoolId);
691
-
1172
+
692
1173
  if (!userPool) {
693
1174
  throw new Error(`User pool ${UserPoolId} not found`);
694
1175
  }
695
-
1176
+
1177
+ const tempPassword = TemporaryPassword || this._generateTemporaryPassword();
1178
+
696
1179
  const userId = uuidv4();
697
1180
  const user = {
698
1181
  Username: Username,
@@ -700,23 +1183,34 @@ class CognitoSimulator {
700
1183
  UserId: userId,
701
1184
  Attributes: this.normalizeUserAttributes(UserAttributes || []),
702
1185
  Enabled: true,
703
- UserStatus: 'FORCE_CHANGE_PASSWORD',
1186
+ UserStatus: "FORCE_CHANGE_PASSWORD",
704
1187
  CreatedDate: new Date().toISOString(),
705
1188
  LastModifiedDate: new Date().toISOString(),
706
- Password: this.hashPassword(TemporaryPassword || 'Temp123!'),
1189
+ Password: this.hashPassword(tempPassword),
707
1190
  MfaOptions: [],
708
1191
  PreferredMfaSetting: null,
709
- UserMFASettingList: []
1192
+ UserMFASettingList: [],
710
1193
  };
711
-
1194
+
1195
+ // PreSignUp trigger — dispara antes de criar o usuário (admin context)
1196
+ const preSignUpEvent = this._buildTriggerEvent("PreSignUp_AdminCreateUser", userPool, user, "ADMIN", {
1197
+ userAttributes: this.normalizeUserAttributes(UserAttributes || []),
1198
+ validationData: {},
1199
+ clientMetadata: {},
1200
+ });
1201
+ // Fire and forget — AdminCreateUser PreSignUp errors are non-blocking in local sim
1202
+ this._invokeTrigger(userPool, "PreSignUp", preSignUpEvent).catch((err) => {
1203
+ logger.warn(`PreSignUp trigger error on AdminCreateUser (ignored): ${err.message}`);
1204
+ });
1205
+
712
1206
  this.users.set(userId, user);
713
1207
  userPool.Users.push(userId);
714
1208
  userPool.EstimatedNumberOfUsers++;
715
1209
  this.persistUsers();
716
1210
  this.persistUserPools();
717
-
718
- logger.debug(`👤 Usuário administrador criado: ${Username}`);
719
-
1211
+
1212
+ logger.info(`👤 Usuário criado: ${Username} | Senha temporária: ${tempPassword}`);
1213
+
720
1214
  return {
721
1215
  User: {
722
1216
  Username: user.Username,
@@ -724,37 +1218,50 @@ class CognitoSimulator {
724
1218
  UserCreateDate: user.CreatedDate,
725
1219
  UserLastModifiedDate: user.LastModifiedDate,
726
1220
  Enabled: user.Enabled,
727
- UserStatus: user.UserStatus
728
- }
1221
+ UserStatus: user.UserStatus,
1222
+ TemporaryPassword: tempPassword,
1223
+ },
729
1224
  };
730
1225
  }
731
1226
 
1227
+ _generateTemporaryPassword() {
1228
+ const upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1229
+ const lower = "abcdefghijklmnopqrstuvwxyz";
1230
+ const digits = "0123456789";
1231
+ const special = "!@#$%^&*";
1232
+ const all = upper + lower + digits + special;
1233
+ const rand = (set) => set[Math.floor(Math.random() * set.length)];
1234
+ const password = [rand(upper), rand(lower), rand(digits), rand(special)];
1235
+ for (let i = 4; i < 10; i++) password.push(rand(all));
1236
+ return password.sort(() => Math.random() - 0.5).join("");
1237
+ }
1238
+
732
1239
  adminSetUserPassword(params) {
733
1240
  const { UserPoolId, Username, Password, Permanent } = params;
734
1241
  const user = this.findUserByUsername(Username, null, UserPoolId);
735
-
1242
+
736
1243
  if (!user) {
737
1244
  throw new Error(`User not found: ${Username}`);
738
1245
  }
739
-
1246
+
740
1247
  user.Password = this.hashPassword(Password);
741
1248
  if (Permanent) {
742
- user.UserStatus = 'CONFIRMED';
1249
+ user.UserStatus = "CONFIRMED";
743
1250
  }
744
1251
  user.LastModifiedDate = new Date().toISOString();
745
1252
  this.persistUsers();
746
-
1253
+
747
1254
  return {};
748
1255
  }
749
1256
 
750
1257
  adminDeleteUser(params) {
751
1258
  const { UserPoolId, Username } = params;
752
1259
  const user = this.findUserByUsername(Username, null, UserPoolId);
753
-
1260
+
754
1261
  if (!user) {
755
1262
  throw new Error(`User not found: ${Username}`);
756
1263
  }
757
-
1264
+
758
1265
  const userPool = this.userPools.get(UserPoolId);
759
1266
  if (userPool) {
760
1267
  const index = userPool.Users.indexOf(user.UserId);
@@ -763,11 +1270,11 @@ class CognitoSimulator {
763
1270
  userPool.EstimatedNumberOfUsers--;
764
1271
  }
765
1272
  }
766
-
1273
+
767
1274
  this.users.delete(user.UserId);
768
1275
  this.persistUsers();
769
1276
  this.persistUserPools();
770
-
1277
+
771
1278
  return {};
772
1279
  }
773
1280
 
@@ -784,20 +1291,21 @@ class CognitoSimulator {
784
1291
 
785
1292
  findUserByUsername(username, clientId = null, userPoolId = null) {
786
1293
  let targetUserPoolId = userPoolId;
787
-
1294
+
788
1295
  if (clientId && !targetUserPoolId) {
789
1296
  const userPool = this.findUserPoolByClientId(clientId);
790
1297
  if (userPool) {
791
1298
  targetUserPoolId = userPool.Id;
792
1299
  }
793
1300
  }
794
-
1301
+
795
1302
  for (const user of this.users.values()) {
796
- if (user.Username === username && user.UserPoolId === targetUserPoolId) {
797
- return user;
798
- }
1303
+ if (user.UserPoolId !== targetUserPoolId) continue;
1304
+ // Match by Username or by email attribute (when UsernameAttributes includes 'email')
1305
+ if (user.Username === username) return user;
1306
+ if (user.Attributes?.email === username) return user;
799
1307
  }
800
-
1308
+
801
1309
  return null;
802
1310
  }
803
1311
 
@@ -815,7 +1323,7 @@ class CognitoSimulator {
815
1323
 
816
1324
  hashPassword(password) {
817
1325
  // Simulação de hash (não usar em produção real)
818
- return crypto.createHash('sha256').update(password).digest('hex');
1326
+ return crypto.createHash("sha256").update(password).digest("hex");
819
1327
  }
820
1328
 
821
1329
  verifyPassword(password, hash) {
@@ -826,7 +1334,7 @@ class CognitoSimulator {
826
1334
 
827
1335
  createIdentityPool(params) {
828
1336
  const { IdentityPoolName, AllowUnauthenticatedIdentities, SupportedLoginProviders, CognitoIdentityProviders } = params;
829
-
1337
+
830
1338
  const identityPoolId = `local:${IdentityPoolName}_${Date.now()}`;
831
1339
  const identityPool = {
832
1340
  IdentityPoolId: identityPoolId,
@@ -834,31 +1342,31 @@ class CognitoSimulator {
834
1342
  AllowUnauthenticatedIdentities: AllowUnauthenticatedIdentities || false,
835
1343
  SupportedLoginProviders: SupportedLoginProviders || {},
836
1344
  CognitoIdentityProviders: CognitoIdentityProviders || [],
837
- Identities: new Map()
1345
+ Identities: new Map(),
838
1346
  };
839
-
1347
+
840
1348
  this.identityPools.set(identityPoolId, identityPool);
841
1349
  this.persistIdentityPools();
842
-
1350
+
843
1351
  logger.debug(`✅ Identity Pool criado: ${IdentityPoolName} (${identityPoolId})`);
844
-
1352
+
845
1353
  return {
846
1354
  IdentityPoolId: identityPoolId,
847
1355
  IdentityPoolName: identityPoolName,
848
- AllowUnauthenticatedIdentities: identityPool.AllowUnauthenticatedIdentities
1356
+ AllowUnauthenticatedIdentities: identityPool.AllowUnauthenticatedIdentities,
849
1357
  };
850
1358
  }
851
1359
 
852
1360
  getId(params) {
853
1361
  const { IdentityPoolId, Logins } = params;
854
1362
  const identityPool = this.identityPools.get(IdentityPoolId);
855
-
1363
+
856
1364
  if (!identityPool) {
857
1365
  throw new Error(`Identity pool ${IdentityPoolId} not found`);
858
1366
  }
859
-
1367
+
860
1368
  let identityId = null;
861
-
1369
+
862
1370
  if (Logins) {
863
1371
  // Procura identidade existente com os logins fornecidos
864
1372
  for (const [id, identity] of identityPool.Identities) {
@@ -868,47 +1376,47 @@ class CognitoSimulator {
868
1376
  }
869
1377
  }
870
1378
  }
871
-
1379
+
872
1380
  if (!identityId) {
873
1381
  identityId = uuidv4();
874
1382
  identityPool.Identities.set(identityId, {
875
1383
  IdentityId: identityId,
876
1384
  Logins: Logins || {},
877
1385
  CreationDate: new Date().toISOString(),
878
- LastModifiedDate: new Date().toISOString()
1386
+ LastModifiedDate: new Date().toISOString(),
879
1387
  });
880
1388
  this.persistIdentityPools();
881
1389
  }
882
-
1390
+
883
1391
  return {
884
- IdentityId: identityId
1392
+ IdentityId: identityId,
885
1393
  };
886
1394
  }
887
1395
 
888
1396
  getCredentialsForIdentity(params) {
889
1397
  const { IdentityId, Logins } = params;
890
1398
  const identityPool = this.findIdentityPoolByIdentityId(IdentityId);
891
-
1399
+
892
1400
  if (!identityPool) {
893
1401
  throw new Error(`Identity ${IdentityId} not found`);
894
1402
  }
895
-
1403
+
896
1404
  const identity = identityPool.Identities.get(IdentityId);
897
1405
  if (!identity) {
898
1406
  throw new Error(`Identity ${IdentityId} not found in pool`);
899
1407
  }
900
-
1408
+
901
1409
  // Gera credenciais temporárias (simuladas)
902
1410
  const credentials = {
903
- AccessKeyId: `AKIA${crypto.randomBytes(16).toString('hex').toUpperCase()}`,
904
- SecretKey: crypto.randomBytes(32).toString('hex'),
905
- SessionToken: crypto.randomBytes(64).toString('base64'),
906
- Expiration: new Date(Date.now() + 3600000).toISOString()
1411
+ AccessKeyId: `AKIA${crypto.randomBytes(16).toString("hex").toUpperCase()}`,
1412
+ SecretKey: crypto.randomBytes(32).toString("hex"),
1413
+ SessionToken: crypto.randomBytes(64).toString("base64"),
1414
+ Expiration: new Date(Date.now() + 3600000).toISOString(),
907
1415
  };
908
-
1416
+
909
1417
  return {
910
1418
  Credentials: credentials,
911
- IdentityId: IdentityId
1419
+ IdentityId: IdentityId,
912
1420
  };
913
1421
  }
914
1422
 
@@ -924,15 +1432,15 @@ class CognitoSimulator {
924
1432
  matchesLogins(existingLogins, newLogins) {
925
1433
  const existingKeys = Object.keys(existingLogins);
926
1434
  const newKeys = Object.keys(newLogins);
927
-
1435
+
928
1436
  if (existingKeys.length !== newKeys.length) return false;
929
-
1437
+
930
1438
  for (const key of existingKeys) {
931
1439
  if (existingLogins[key] !== newLogins[key]) {
932
1440
  return false;
933
1441
  }
934
1442
  }
935
-
1443
+
936
1444
  return true;
937
1445
  }
938
1446
 
@@ -940,7 +1448,7 @@ class CognitoSimulator {
940
1448
 
941
1449
  loadUserPools() {
942
1450
  // Load persisted pools first
943
- const saved = this.store.read('__userpools__');
1451
+ const saved = this.store.read("__userpools__");
944
1452
  if (saved) {
945
1453
  for (const [id, data] of Object.entries(saved)) {
946
1454
  data.Clients = new Map(Object.entries(data.Clients || {}));
@@ -958,19 +1466,23 @@ class CognitoSimulator {
958
1466
 
959
1467
  for (let i = 0; i < this.config.cognito.userPools.length; i++) {
960
1468
  const poolConfig = this.config.cognito.userPools[i];
961
- const existing = Array.from(this.userPools.values()).find(p => p.Name === poolConfig.PoolName);
1469
+ const existing = Array.from(this.userPools.values()).find((p) => p.Name === poolConfig.PoolName);
962
1470
 
963
1471
  if (!existing) {
964
1472
  const result = this.createUserPool(poolConfig);
965
1473
  const poolId = result.UserPool.Id;
966
1474
  logger.debug(`✅ User Pool criado a partir da config: ${poolConfig.PoolName} (${poolId})`);
967
1475
 
1476
+ // Copy LambdaTriggers from config onto the pool object
1477
+ const pool = this.userPools.get(poolId);
1478
+ pool.LambdaTriggers = poolConfig.LambdaTriggers || {};
1479
+
968
1480
  // Auto-create a default client if not specified
969
1481
  if (!poolConfig.ClientId) {
970
1482
  const clientResult = this.createUserPoolClient({
971
1483
  UserPoolId: poolId,
972
1484
  ClientName: `${poolConfig.PoolName}-client`,
973
- GenerateSecret: false
1485
+ GenerateSecret: false,
974
1486
  });
975
1487
  const clientId = clientResult.UserPoolClient.ClientId;
976
1488
  this.config.cognito.userPools[i].ClientId = clientId;
@@ -978,13 +1490,37 @@ class CognitoSimulator {
978
1490
  configChanged = true;
979
1491
  logger.debug(`✅ Client criado automaticamente: ${clientId}`);
980
1492
  }
981
- } else if (!poolConfig.ClientId) {
982
- // Pool exists but no clientId in config — write it back
983
- const firstClient = existing.Clients.size > 0 ? existing.Clients.values().next().value : null;
984
- if (firstClient) {
985
- this.config.cognito.userPools[i].ClientId = firstClient.ClientId;
986
- this.config.cognito.userPools[i].UserPoolId = existing.Id;
987
- configChanged = true;
1493
+ } else {
1494
+ // Pool already exists apply LambdaTriggers from config
1495
+ existing.LambdaTriggers = poolConfig.LambdaTriggers || {};
1496
+
1497
+ if (!poolConfig.ClientId) {
1498
+ // Pool exists but no clientId in config write it back
1499
+ const firstClient = existing.Clients.size > 0 ? existing.Clients.values().next().value : null;
1500
+ if (firstClient) {
1501
+ this.config.cognito.userPools[i].ClientId = firstClient.ClientId;
1502
+ this.config.cognito.userPools[i].UserPoolId = existing.Id;
1503
+ configChanged = true;
1504
+ }
1505
+ } else if (!existing.Clients.has(poolConfig.ClientId)) {
1506
+ // ClientId is in config but not in the pool's Clients map — register it
1507
+ existing.Clients.set(poolConfig.ClientId, {
1508
+ ClientId: poolConfig.ClientId,
1509
+ ClientName: `${poolConfig.PoolName}-client`,
1510
+ ClientSecret: null,
1511
+ UserPoolId: existing.Id,
1512
+ RefreshTokenValidity: 30,
1513
+ AccessTokenValidity: 1,
1514
+ IdTokenValidity: 1,
1515
+ AllowedOAuthFlows: ['code'],
1516
+ AllowedOAuthScopes: ['openid', 'email', 'profile'],
1517
+ CallbackURLs: [],
1518
+ LogoutURLs: [],
1519
+ CreatedDate: new Date().toISOString(),
1520
+ LastModifiedDate: new Date().toISOString(),
1521
+ });
1522
+ this.persistUserPools();
1523
+ logger.debug(`✅ ClientId ${poolConfig.ClientId} registrado no pool ${existing.Id}`);
988
1524
  }
989
1525
  }
990
1526
  }
@@ -992,14 +1528,14 @@ class CognitoSimulator {
992
1528
  // Write clientId back to aws-local-simulator.json
993
1529
  if (configChanged && configPath) {
994
1530
  try {
995
- const fs = require('fs');
996
- const fileContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
1531
+ const fs = require("fs");
1532
+ const fileContent = JSON.parse(fs.readFileSync(configPath, "utf8"));
997
1533
  fileContent.cognito = fileContent.cognito || {};
998
- fileContent.cognito.userPools = this.config.cognito.userPools.map(p => ({
1534
+ fileContent.cognito.userPools = this.config.cognito.userPools.map((p) => ({
999
1535
  PoolName: p.PoolName,
1000
1536
  AutoVerifiedAttributes: p.AutoVerifiedAttributes,
1001
1537
  UserPoolId: p.UserPoolId,
1002
- ClientId: p.ClientId
1538
+ ClientId: p.ClientId,
1003
1539
  }));
1004
1540
  fs.writeFileSync(configPath, JSON.stringify(fileContent, null, 2));
1005
1541
  logger.info(`✅ ClientId gravado em: ${configPath}`);
@@ -1011,7 +1547,7 @@ class CognitoSimulator {
1011
1547
  }
1012
1548
 
1013
1549
  loadIdentityPools() {
1014
- const saved = this.store.read('__identitypools__');
1550
+ const saved = this.store.read("__identitypools__");
1015
1551
  if (saved) {
1016
1552
  for (const [id, data] of Object.entries(saved)) {
1017
1553
  data.Identities = new Map(Object.entries(data.Identities || {}));
@@ -1021,7 +1557,7 @@ class CognitoSimulator {
1021
1557
  }
1022
1558
 
1023
1559
  loadUsers() {
1024
- const saved = this.store.read('__users__');
1560
+ const saved = this.store.read("__users__");
1025
1561
  if (saved) {
1026
1562
  for (const [id, user] of Object.entries(saved)) {
1027
1563
  this.users.set(id, user);
@@ -1030,7 +1566,7 @@ class CognitoSimulator {
1030
1566
  }
1031
1567
 
1032
1568
  loadSessions() {
1033
- const saved = this.store.read('__sessions__');
1569
+ const saved = this.store.read("__sessions__");
1034
1570
  if (saved) {
1035
1571
  for (const [id, session] of Object.entries(saved)) {
1036
1572
  this.sessions.set(id, session);
@@ -1048,10 +1584,10 @@ class CognitoSimulator {
1048
1584
  Clients: Object.fromEntries(pool.Clients),
1049
1585
  Groups: Object.fromEntries(pool.Groups),
1050
1586
  IdentityProviders: Object.fromEntries(pool.IdentityProviders),
1051
- ResourceServers: Object.fromEntries(pool.ResourceServers)
1587
+ ResourceServers: Object.fromEntries(pool.ResourceServers),
1052
1588
  };
1053
1589
  }
1054
- this.store.write('__userpools__', poolsObj);
1590
+ this.store.write("__userpools__", poolsObj);
1055
1591
  }
1056
1592
 
1057
1593
  persistIdentityPools() {
@@ -1059,10 +1595,10 @@ class CognitoSimulator {
1059
1595
  for (const [id, pool] of this.identityPools.entries()) {
1060
1596
  poolsObj[id] = {
1061
1597
  ...pool,
1062
- Identities: Object.fromEntries(pool.Identities)
1598
+ Identities: Object.fromEntries(pool.Identities),
1063
1599
  };
1064
1600
  }
1065
- this.store.write('__identitypools__', poolsObj);
1601
+ this.store.write("__identitypools__", poolsObj);
1066
1602
  }
1067
1603
 
1068
1604
  persistUsers() {
@@ -1070,7 +1606,7 @@ class CognitoSimulator {
1070
1606
  for (const [id, user] of this.users.entries()) {
1071
1607
  usersObj[id] = user;
1072
1608
  }
1073
- this.store.write('__users__', usersObj);
1609
+ this.store.write("__users__", usersObj);
1074
1610
  }
1075
1611
 
1076
1612
  persistSessions() {
@@ -1078,7 +1614,7 @@ class CognitoSimulator {
1078
1614
  for (const [id, session] of this.sessions.entries()) {
1079
1615
  sessionsObj[id] = session;
1080
1616
  }
1081
- this.store.write('__sessions__', sessionsObj);
1617
+ this.store.write("__sessions__", sessionsObj);
1082
1618
  }
1083
1619
 
1084
1620
  async reset() {
@@ -1088,13 +1624,13 @@ class CognitoSimulator {
1088
1624
  this.sessions.clear();
1089
1625
  this.accessTokens.clear();
1090
1626
  this.refreshTokens.clear();
1091
-
1627
+
1092
1628
  this.persistUserPools();
1093
1629
  this.persistIdentityPools();
1094
1630
  this.persistUsers();
1095
1631
  this.persistSessions();
1096
-
1097
- logger.debug('Cognito: Todos os dados resetados');
1632
+
1633
+ logger.debug("Cognito: Todos os dados resetados");
1098
1634
  }
1099
1635
 
1100
1636
  // ============ Stats ============
@@ -1116,4 +1652,4 @@ class CognitoSimulator {
1116
1652
  }
1117
1653
  }
1118
1654
 
1119
- module.exports = CognitoSimulator;
1655
+ module.exports = CognitoSimulator;