@massimo.mazzoleni/cognito-max 1.0.0

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.
Files changed (64) hide show
  1. package/README.md +2410 -0
  2. package/dist/chunk-AD7T42HJ.js +3 -0
  3. package/dist/chunk-AD7T42HJ.js.map +1 -0
  4. package/dist/chunk-DKPFVGTY.js +683 -0
  5. package/dist/chunk-DKPFVGTY.js.map +1 -0
  6. package/dist/chunk-N4OQLBV6.js +135 -0
  7. package/dist/chunk-N4OQLBV6.js.map +1 -0
  8. package/dist/client-63FraVdm.d.ts +69 -0
  9. package/dist/client-BAoL8h4E.d.cts +69 -0
  10. package/dist/core/index.cjs +696 -0
  11. package/dist/core/index.cjs.map +1 -0
  12. package/dist/core/index.d.cts +3 -0
  13. package/dist/core/index.d.ts +3 -0
  14. package/dist/core/index.js +4 -0
  15. package/dist/core/index.js.map +1 -0
  16. package/dist/errors-BkUDHleb.d.cts +22 -0
  17. package/dist/errors-BkUDHleb.d.ts +22 -0
  18. package/dist/index.cjs +696 -0
  19. package/dist/index.cjs.map +1 -0
  20. package/dist/index.d.cts +3 -0
  21. package/dist/index.d.ts +3 -0
  22. package/dist/index.js +4 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/react/index.cjs +844 -0
  25. package/dist/react/index.cjs.map +1 -0
  26. package/dist/react/index.d.cts +104 -0
  27. package/dist/react/index.d.ts +104 -0
  28. package/dist/react/index.js +64 -0
  29. package/dist/react/index.js.map +1 -0
  30. package/dist/types-bxA1vonL.d.cts +113 -0
  31. package/dist/types-bxA1vonL.d.ts +113 -0
  32. package/dist/ui/index.cjs +1183 -0
  33. package/dist/ui/index.cjs.map +1 -0
  34. package/dist/ui/index.d.cts +241 -0
  35. package/dist/ui/index.d.ts +241 -0
  36. package/dist/ui/index.js +1109 -0
  37. package/dist/ui/index.js.map +1 -0
  38. package/package.json +81 -0
  39. package/src/core/client.ts +604 -0
  40. package/src/core/errors.ts +91 -0
  41. package/src/core/event-bus.ts +41 -0
  42. package/src/core/index.ts +5 -0
  43. package/src/core/internal/converters.ts +32 -0
  44. package/src/core/storage.ts +79 -0
  45. package/src/core/types.ts +87 -0
  46. package/src/index.ts +1 -0
  47. package/src/react/components/ProtectedRoute.tsx +56 -0
  48. package/src/react/context.tsx +126 -0
  49. package/src/react/hooks/useAuth.ts +75 -0
  50. package/src/react/hooks/useMfa.ts +19 -0
  51. package/src/react/hooks/useSession.ts +16 -0
  52. package/src/react/hooks/useUser.ts +24 -0
  53. package/src/react/index.ts +10 -0
  54. package/src/ui/components/ChangePasswordForm.tsx +105 -0
  55. package/src/ui/components/ForgotPasswordForm.tsx +159 -0
  56. package/src/ui/components/MfaSetupWizard.tsx +136 -0
  57. package/src/ui/components/RegisterForm.tsx +159 -0
  58. package/src/ui/components/SignInForm.tsx +296 -0
  59. package/src/ui/hooks/useChangePasswordForm.ts +81 -0
  60. package/src/ui/hooks/useForgotPasswordForm.ts +109 -0
  61. package/src/ui/hooks/useMfaSetup.ts +93 -0
  62. package/src/ui/hooks/useRegisterForm.ts +120 -0
  63. package/src/ui/hooks/useSignInForm.ts +245 -0
  64. package/src/ui/index.ts +31 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,696 @@
1
+ 'use strict';
2
+
3
+ var amazonCognitoIdentityJs = require('amazon-cognito-identity-js');
4
+ var clientCognitoIdentityProvider = require('@aws-sdk/client-cognito-identity-provider');
5
+
6
+ // src/core/errors.ts
7
+ var CognitoAuthError = class extends Error {
8
+ constructor(message, code, originalError) {
9
+ super(message);
10
+ this.code = code;
11
+ this.originalError = originalError;
12
+ this.name = "CognitoAuthError";
13
+ Object.setPrototypeOf(this, new.target.prototype);
14
+ }
15
+ };
16
+ var SessionExpiredError = class extends CognitoAuthError {
17
+ constructor() {
18
+ super("La sessione \xE8 scaduta, effettua nuovamente il login", "SESSION_EXPIRED");
19
+ Object.setPrototypeOf(this, new.target.prototype);
20
+ }
21
+ };
22
+ var NotAuthorizedError = class extends CognitoAuthError {
23
+ constructor(message = "Credenziali non valide") {
24
+ super(message, "NOT_AUTHORIZED");
25
+ Object.setPrototypeOf(this, new.target.prototype);
26
+ }
27
+ };
28
+ var UserNotConfirmedError = class extends CognitoAuthError {
29
+ constructor() {
30
+ super("Account non confermato. Controlla la tua email.", "USER_NOT_CONFIRMED");
31
+ Object.setPrototypeOf(this, new.target.prototype);
32
+ }
33
+ };
34
+ var InvalidCodeError = class extends CognitoAuthError {
35
+ constructor(expired = false) {
36
+ super(
37
+ expired ? "Il codice \xE8 scaduto" : "Codice non valido",
38
+ expired ? "EXPIRED_CODE" : "CODE_MISMATCH"
39
+ );
40
+ Object.setPrototypeOf(this, new.target.prototype);
41
+ }
42
+ };
43
+ function mapCognitoError(error) {
44
+ if (error instanceof CognitoAuthError) return error;
45
+ const name = error?.name ?? error?.code ?? "";
46
+ const message = error?.message ?? "Si \xE8 verificato un errore di autenticazione";
47
+ const codeMap = {
48
+ NotAuthorizedException: "NOT_AUTHORIZED",
49
+ UserNotFoundException: "USER_NOT_FOUND",
50
+ UserNotConfirmedException: "USER_NOT_CONFIRMED",
51
+ InvalidParameterException: "INVALID_PARAMETER",
52
+ InvalidPasswordException: "INVALID_PASSWORD",
53
+ CodeMismatchException: "CODE_MISMATCH",
54
+ ExpiredCodeException: "EXPIRED_CODE",
55
+ CodeDeliveryFailureException: "CODE_DELIVERY_FAILURE",
56
+ LimitExceededException: "LIMIT_EXCEEDED",
57
+ TooManyRequestsException: "TOO_MANY_REQUESTS",
58
+ TooManyFailedAttemptsException: "TOO_MANY_FAILED_ATTEMPTS",
59
+ PasswordResetRequiredException: "PASSWORD_RESET_REQUIRED",
60
+ MFAMethodNotFoundException: "MFA_METHOD_NOT_FOUND",
61
+ SoftwareTokenMFANotFoundException: "SOFTWARE_TOKEN_MFA_NOT_FOUND",
62
+ NetworkError: "NETWORK_ERROR",
63
+ FetchError: "NETWORK_ERROR"
64
+ };
65
+ return new CognitoAuthError(message, codeMap[name] ?? "UNKNOWN", error);
66
+ }
67
+
68
+ // src/core/storage.ts
69
+ var LocalStorageAdapter = class {
70
+ getItem(key) {
71
+ return localStorage.getItem(key);
72
+ }
73
+ setItem(key, value) {
74
+ localStorage.setItem(key, value);
75
+ }
76
+ removeItem(key) {
77
+ localStorage.removeItem(key);
78
+ }
79
+ clear() {
80
+ localStorage.clear();
81
+ }
82
+ };
83
+ var SessionStorageAdapter = class {
84
+ getItem(key) {
85
+ return sessionStorage.getItem(key);
86
+ }
87
+ setItem(key, value) {
88
+ sessionStorage.setItem(key, value);
89
+ }
90
+ removeItem(key) {
91
+ sessionStorage.removeItem(key);
92
+ }
93
+ clear() {
94
+ sessionStorage.clear();
95
+ }
96
+ };
97
+ var InMemoryStorageAdapter = class {
98
+ constructor() {
99
+ this.store = /* @__PURE__ */ new Map();
100
+ }
101
+ getItem(key) {
102
+ return this.store.get(key) ?? null;
103
+ }
104
+ setItem(key, value) {
105
+ this.store.set(key, value);
106
+ }
107
+ removeItem(key) {
108
+ this.store.delete(key);
109
+ }
110
+ clear() {
111
+ this.store.clear();
112
+ }
113
+ };
114
+ var AutoStorageAdapter = class {
115
+ constructor() {
116
+ this.delegate = typeof window !== "undefined" && window.localStorage ? new LocalStorageAdapter() : new InMemoryStorageAdapter();
117
+ }
118
+ getItem(key) {
119
+ return this.delegate.getItem(key);
120
+ }
121
+ setItem(key, value) {
122
+ this.delegate.setItem(key, value);
123
+ }
124
+ removeItem(key) {
125
+ this.delegate.removeItem(key);
126
+ }
127
+ clear() {
128
+ this.delegate.clear?.();
129
+ }
130
+ };
131
+
132
+ // src/core/event-bus.ts
133
+ var TypedEventEmitter = class {
134
+ constructor() {
135
+ this._listeners = /* @__PURE__ */ new Map();
136
+ }
137
+ on(event, listener) {
138
+ if (!this._listeners.has(event)) {
139
+ this._listeners.set(event, /* @__PURE__ */ new Set());
140
+ }
141
+ this._listeners.get(event).add(listener);
142
+ return () => this.off(event, listener);
143
+ }
144
+ off(event, listener) {
145
+ this._listeners.get(event)?.delete(listener);
146
+ }
147
+ once(event, listener) {
148
+ const unsub = this.on(event, ((...args) => {
149
+ unsub();
150
+ listener(...args);
151
+ }));
152
+ return unsub;
153
+ }
154
+ emit(event, ...args) {
155
+ this._listeners.get(event)?.forEach((l) => l(...args));
156
+ }
157
+ removeAllListeners(event) {
158
+ if (event !== void 0) {
159
+ this._listeners.delete(event);
160
+ } else {
161
+ this._listeners.clear();
162
+ }
163
+ }
164
+ };
165
+
166
+ // src/core/internal/converters.ts
167
+ function buildAuthSession(session) {
168
+ const expiresAt = new Date(session.getAccessToken().getExpiration() * 1e3);
169
+ return {
170
+ accessToken: session.getAccessToken().getJwtToken(),
171
+ idToken: session.getIdToken().getJwtToken(),
172
+ refreshToken: session.getRefreshToken().getToken(),
173
+ expiresAt,
174
+ isValid: () => session.isValid()
175
+ };
176
+ }
177
+ function buildAuthUser(session, fallbackUsername) {
178
+ const payload = session.getIdToken().decodePayload();
179
+ const str = (v) => v != null ? String(v) : "";
180
+ const bool = (v) => v === true || v === "true";
181
+ return {
182
+ sub: str(payload["sub"]),
183
+ username: str(payload["cognito:username"] ?? fallbackUsername),
184
+ email: str(payload["email"]),
185
+ emailVerified: bool(payload["email_verified"]),
186
+ groups: Array.isArray(payload["cognito:groups"]) ? payload["cognito:groups"] : [],
187
+ attributes: Object.fromEntries(Object.entries(payload).map(([k, v]) => [k, str(v)]))
188
+ };
189
+ }
190
+
191
+ // src/core/client.ts
192
+ function validateConfig(config) {
193
+ const missing = ["userPoolId", "clientId", "region"].filter((k) => !config[k]);
194
+ if (missing.length) {
195
+ throw new CognitoAuthError(
196
+ `AuthConfig incompleto \u2014 campi obbligatori mancanti: ${missing.join(", ")}`,
197
+ "INVALID_PARAMETER"
198
+ );
199
+ }
200
+ if (!/^[a-z]{2}-[a-z]+-\d$/.test(config.region)) {
201
+ throw new CognitoAuthError(
202
+ `AuthConfig.region non valido: "${config.region}" (es. eu-west-1)`,
203
+ "INVALID_PARAMETER"
204
+ );
205
+ }
206
+ }
207
+ var CognitoAuthClient = class extends TypedEventEmitter {
208
+ constructor(config) {
209
+ super();
210
+ this._state = "idle";
211
+ this._idpClient = null;
212
+ // Mappa challengeSession-id → CognitoUser in-flight (MFA / new-password challenge)
213
+ this._pendingChallenges = /* @__PURE__ */ new Map();
214
+ this._refreshTimer = null;
215
+ validateConfig(config);
216
+ this.config = {
217
+ autoRefresh: true,
218
+ refreshMarginSeconds: 300,
219
+ totpIssuer: config.clientId,
220
+ storage: new AutoStorageAdapter(),
221
+ ...config
222
+ };
223
+ this._pool = new amazonCognitoIdentityJs.CognitoUserPool({
224
+ UserPoolId: this.config.userPoolId,
225
+ ClientId: this.config.clientId,
226
+ // amazon-cognito-identity-js ICognitoStorage coincide con il nostro StorageAdapter
227
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
228
+ Storage: this.config.storage
229
+ });
230
+ }
231
+ // ─── State ─────────────────────────────────────────────────────────────────
232
+ get state() {
233
+ return this._state;
234
+ }
235
+ setState(next) {
236
+ if (this._state === next) return;
237
+ this._state = next;
238
+ this.emit("stateChanged", next);
239
+ }
240
+ // ─── Sign-in ───────────────────────────────────────────────────────────────
241
+ async signIn(email, password) {
242
+ this.setState("loading");
243
+ const cognitoUser = this._makeCognitoUser(email);
244
+ const authDetails = new amazonCognitoIdentityJs.AuthenticationDetails({ Username: email, Password: password });
245
+ return new Promise((resolve, reject) => {
246
+ cognitoUser.authenticateUser(authDetails, {
247
+ onSuccess: (session) => {
248
+ resolve(this._onAuthSuccess(cognitoUser, session));
249
+ },
250
+ onFailure: (err) => {
251
+ this.setState("unauthenticated");
252
+ reject(mapCognitoError(err));
253
+ },
254
+ mfaRequired: () => {
255
+ this.setState("mfa_required");
256
+ const challengeSession = this._storeChallengeUser(cognitoUser);
257
+ const result = { status: "MFA_REQUIRED", mfaType: "SMS", challengeSession };
258
+ this.emit("mfaRequired", { mfaType: "SMS", challengeSession });
259
+ resolve(result);
260
+ },
261
+ totpRequired: () => {
262
+ this.setState("mfa_required");
263
+ const challengeSession = this._storeChallengeUser(cognitoUser);
264
+ const result = { status: "MFA_REQUIRED", mfaType: "TOTP", challengeSession };
265
+ this.emit("mfaRequired", { mfaType: "TOTP", challengeSession });
266
+ resolve(result);
267
+ },
268
+ newPasswordRequired: (_userAttributes, requiredAttributes) => {
269
+ this.setState("new_password_required");
270
+ const challengeSession = this._storeChallengeUser(cognitoUser);
271
+ const required = Array.isArray(requiredAttributes) ? requiredAttributes : [];
272
+ this.emit("newPasswordRequired", { requiredAttributes: required, challengeSession });
273
+ resolve({ status: "NEW_PASSWORD_REQUIRED", requiredAttributes: required, challengeSession });
274
+ },
275
+ // Cognito richiede il setup TOTP prima di completare il login
276
+ mfaSetup: () => {
277
+ this.setState("mfa_required");
278
+ const challengeSession = this._storeChallengeUser(cognitoUser);
279
+ resolve({ status: "MFA_SETUP_REQUIRED", challengeSession });
280
+ }
281
+ });
282
+ });
283
+ }
284
+ async respondToMfaChallenge(challengeSession, code, mfaType) {
285
+ const cognitoUser = this._takeChallengeUser(challengeSession);
286
+ const sdkType = mfaType === "TOTP" ? "SOFTWARE_TOKEN_MFA" : "SMS_MFA";
287
+ return new Promise((resolve, reject) => {
288
+ cognitoUser.sendMFACode(
289
+ code,
290
+ {
291
+ onSuccess: (session) => resolve(this._onAuthSuccess(cognitoUser, session)),
292
+ onFailure: (err) => {
293
+ this.setState("unauthenticated");
294
+ reject(mapCognitoError(err));
295
+ }
296
+ },
297
+ sdkType
298
+ );
299
+ });
300
+ }
301
+ async respondToNewPasswordChallenge(challengeSession, newPassword, userAttributes) {
302
+ const cognitoUser = this._takeChallengeUser(challengeSession);
303
+ return new Promise((resolve, reject) => {
304
+ cognitoUser.completeNewPasswordChallenge(
305
+ newPassword,
306
+ userAttributes ?? {},
307
+ // es. { name, family_name, given_name }
308
+ {
309
+ onSuccess: (session) => resolve(this._onAuthSuccess(cognitoUser, session)),
310
+ onFailure: (err) => {
311
+ this.setState("unauthenticated");
312
+ reject(mapCognitoError(err));
313
+ },
314
+ // Cognito può richiedere MFA anche dopo il cambio password forzato
315
+ mfaRequired: () => {
316
+ this.setState("mfa_required");
317
+ const newSession = this._storeChallengeUser(cognitoUser);
318
+ this.emit("mfaRequired", { mfaType: "SMS", challengeSession: newSession });
319
+ resolve({ status: "MFA_REQUIRED", mfaType: "SMS", challengeSession: newSession });
320
+ },
321
+ totpRequired: () => {
322
+ this.setState("mfa_required");
323
+ const newSession = this._storeChallengeUser(cognitoUser);
324
+ this.emit("mfaRequired", { mfaType: "TOTP", challengeSession: newSession });
325
+ resolve({ status: "MFA_REQUIRED", mfaType: "TOTP", challengeSession: newSession });
326
+ }
327
+ }
328
+ );
329
+ });
330
+ }
331
+ async signOut(global = false) {
332
+ this._clearRefreshTimer();
333
+ this._pendingChallenges.clear();
334
+ const cognitoUser = this._pool.getCurrentUser();
335
+ if (!cognitoUser) {
336
+ this.setState("unauthenticated");
337
+ this.emit("signedOut");
338
+ return;
339
+ }
340
+ if (global) {
341
+ await new Promise((resolve, reject) => {
342
+ cognitoUser.globalSignOut({
343
+ onSuccess: () => {
344
+ this.setState("unauthenticated");
345
+ this.emit("signedOut");
346
+ resolve();
347
+ },
348
+ onFailure: (err) => reject(mapCognitoError(err))
349
+ });
350
+ });
351
+ } else {
352
+ cognitoUser.signOut();
353
+ this.setState("unauthenticated");
354
+ this.emit("signedOut");
355
+ }
356
+ }
357
+ // ─── Registration ──────────────────────────────────────────────────────────
358
+ async signUp(email, password, attributes = {}) {
359
+ const userAttributes = Object.entries({ email, ...attributes }).map(
360
+ ([Name, Value]) => new amazonCognitoIdentityJs.CognitoUserAttribute({ Name, Value })
361
+ );
362
+ await new Promise((resolve, reject) => {
363
+ this._pool.signUp(email, password, userAttributes, [], (err) => {
364
+ if (err) return reject(mapCognitoError(err));
365
+ resolve();
366
+ });
367
+ });
368
+ }
369
+ async confirmSignUp(email, code) {
370
+ const cognitoUser = this._makeCognitoUser(email);
371
+ await new Promise((resolve, reject) => {
372
+ cognitoUser.confirmRegistration(code, true, (err) => {
373
+ if (err) return reject(mapCognitoError(err));
374
+ resolve();
375
+ });
376
+ });
377
+ }
378
+ async resendConfirmationCode(email) {
379
+ const cognitoUser = this._makeCognitoUser(email);
380
+ await new Promise((resolve, reject) => {
381
+ cognitoUser.resendConfirmationCode((err) => {
382
+ if (err) return reject(mapCognitoError(err));
383
+ resolve();
384
+ });
385
+ });
386
+ }
387
+ // ─── Password ──────────────────────────────────────────────────────────────
388
+ async forgotPassword(email) {
389
+ const cognitoUser = this._makeCognitoUser(email);
390
+ await new Promise((resolve, reject) => {
391
+ cognitoUser.forgotPassword({
392
+ // inputVerificationCode → il codice è stato inviato all'email/telefono
393
+ inputVerificationCode: () => resolve(),
394
+ onSuccess: () => resolve(),
395
+ onFailure: (err) => reject(mapCognitoError(err))
396
+ });
397
+ });
398
+ }
399
+ async confirmForgotPassword(email, code, newPassword) {
400
+ const cognitoUser = this._makeCognitoUser(email);
401
+ await new Promise((resolve, reject) => {
402
+ cognitoUser.confirmPassword(code, newPassword, {
403
+ onSuccess: () => resolve(),
404
+ onFailure: (err) => reject(mapCognitoError(err))
405
+ });
406
+ });
407
+ }
408
+ async changePassword(currentPassword, newPassword) {
409
+ const cognitoUser = await this._getAuthenticatedUser();
410
+ await new Promise((resolve, reject) => {
411
+ cognitoUser.changePassword(currentPassword, newPassword, (err) => {
412
+ if (err) return reject(mapCognitoError(err));
413
+ resolve();
414
+ });
415
+ });
416
+ }
417
+ // ─── Session & User ────────────────────────────────────────────────────────
418
+ async getSession() {
419
+ const cognitoUser = this._pool.getCurrentUser();
420
+ if (!cognitoUser) throw new SessionExpiredError();
421
+ const raw = await this._getRawSession(cognitoUser);
422
+ this._scheduleRefresh(cognitoUser, raw);
423
+ return buildAuthSession(raw);
424
+ }
425
+ async getCurrentUser() {
426
+ const cognitoUser = this._pool.getCurrentUser();
427
+ if (!cognitoUser) return null;
428
+ try {
429
+ const session = await this._getRawSession(cognitoUser);
430
+ this.setState("authenticated");
431
+ return buildAuthUser(session, cognitoUser.getUsername());
432
+ } catch {
433
+ return null;
434
+ }
435
+ }
436
+ async getUserAttributes() {
437
+ const cognitoUser = await this._getAuthenticatedUser();
438
+ return new Promise((resolve, reject) => {
439
+ cognitoUser.getUserAttributes((err, result) => {
440
+ if (err) return reject(mapCognitoError(err));
441
+ resolve(Object.fromEntries((result ?? []).map((a) => [a.getName(), a.getValue()])));
442
+ });
443
+ });
444
+ }
445
+ async updateUserAttributes(attributes) {
446
+ const cognitoUser = await this._getAuthenticatedUser();
447
+ const attrs = Object.entries(attributes).map(
448
+ ([Name, Value]) => new amazonCognitoIdentityJs.CognitoUserAttribute({ Name, Value })
449
+ );
450
+ await new Promise((resolve, reject) => {
451
+ cognitoUser.updateAttributes(attrs, (err) => {
452
+ if (err) return reject(mapCognitoError(err));
453
+ resolve();
454
+ });
455
+ });
456
+ }
457
+ async verifyUserAttribute(attribute, code) {
458
+ const cognitoUser = await this._getAuthenticatedUser();
459
+ await new Promise((resolve, reject) => {
460
+ cognitoUser.verifyAttribute(attribute, code, {
461
+ onSuccess: () => resolve(),
462
+ onFailure: (err) => reject(mapCognitoError(err))
463
+ });
464
+ });
465
+ }
466
+ async sendAttributeVerificationCode(attribute) {
467
+ const cognitoUser = await this._getAuthenticatedUser();
468
+ await new Promise((resolve, reject) => {
469
+ cognitoUser.getAttributeVerificationCode(attribute, {
470
+ onSuccess: () => resolve(),
471
+ onFailure: (err) => reject(mapCognitoError(err)),
472
+ inputVerificationCode: () => resolve()
473
+ });
474
+ });
475
+ }
476
+ async deleteUser() {
477
+ const cognitoUser = await this._getAuthenticatedUser();
478
+ this._clearRefreshTimer();
479
+ await new Promise((resolve, reject) => {
480
+ cognitoUser.deleteUser((err) => {
481
+ if (err) return reject(mapCognitoError(err));
482
+ this.setState("unauthenticated");
483
+ this.emit("signedOut");
484
+ resolve();
485
+ });
486
+ });
487
+ }
488
+ // ─── MFA ───────────────────────────────────────────────────────────────────
489
+ async setupTotp() {
490
+ const cognitoUser = await this._getAuthenticatedUser();
491
+ return new Promise((resolve, reject) => {
492
+ cognitoUser.associateSoftwareToken({
493
+ associateSecretCode: (secretCode) => {
494
+ const issuer = encodeURIComponent(this.config.totpIssuer);
495
+ const account = encodeURIComponent(cognitoUser.getUsername());
496
+ const qrCodeUri = `otpauth://totp/${issuer}:${account}?secret=${secretCode}&issuer=${issuer}`;
497
+ resolve({ secretCode, qrCodeUri });
498
+ },
499
+ onFailure: (err) => reject(mapCognitoError(err))
500
+ });
501
+ });
502
+ }
503
+ async verifyTotpSetup(code) {
504
+ const cognitoUser = await this._getAuthenticatedUser();
505
+ return new Promise((resolve, reject) => {
506
+ cognitoUser.verifySoftwareToken(code, this.config.totpIssuer, {
507
+ onSuccess: () => resolve(),
508
+ onFailure: (err) => reject(mapCognitoError(err))
509
+ });
510
+ });
511
+ }
512
+ async getMfaPreference() {
513
+ const session = await this.getSession();
514
+ try {
515
+ const response = await this._getIdpClient().send(
516
+ new clientCognitoIdentityProvider.GetUserCommand({ AccessToken: session.accessToken })
517
+ );
518
+ const enabled = response.UserMFASettingList ?? [];
519
+ const preferred = response.PreferredMfaSetting ?? null;
520
+ const toSdkType = (s) => s === "SOFTWARE_TOKEN_MFA" ? "TOTP" : s === "SMS_MFA" ? "SMS" : null;
521
+ return {
522
+ enabled: enabled.length > 0,
523
+ preferred: toSdkType(preferred),
524
+ totp: enabled.includes("SOFTWARE_TOKEN_MFA"),
525
+ sms: enabled.includes("SMS_MFA")
526
+ };
527
+ } catch (err) {
528
+ throw mapCognitoError(err);
529
+ }
530
+ }
531
+ async setMfaPreference(type) {
532
+ const cognitoUser = await this._getAuthenticatedUser();
533
+ const smsMfa = { Enabled: type === "SMS", PreferredMfa: type === "SMS" };
534
+ const totpMfa = { Enabled: type === "TOTP", PreferredMfa: type === "TOTP" };
535
+ return new Promise((resolve, reject) => {
536
+ cognitoUser.setUserMfaPreference(smsMfa, totpMfa, (err) => {
537
+ if (err) return reject(mapCognitoError(err));
538
+ resolve();
539
+ });
540
+ });
541
+ }
542
+ async disableMfa() {
543
+ const cognitoUser = await this._getAuthenticatedUser();
544
+ const off = { Enabled: false, PreferredMfa: false };
545
+ return new Promise((resolve, reject) => {
546
+ cognitoUser.setUserMfaPreference(off, off, (err) => {
547
+ if (err) return reject(mapCognitoError(err));
548
+ resolve();
549
+ });
550
+ });
551
+ }
552
+ // ─── TOTP setup durante il challenge login ─────────────────────────────────
553
+ /**
554
+ * Ottiene il secretCode/QR URI per il setup TOTP durante il flusso di login
555
+ * (quando signIn restituisce MFA_SETUP_REQUIRED). Non richiede una sessione
556
+ * autenticata: usa il CognitoUser in attesa nel challenge.
557
+ */
558
+ async setupTotpChallenge(challengeSession) {
559
+ const cognitoUser = this._peekChallengeUser(challengeSession);
560
+ return new Promise((resolve, reject) => {
561
+ cognitoUser.associateSoftwareToken({
562
+ associateSecretCode: (secretCode) => {
563
+ const issuer = encodeURIComponent(this.config.totpIssuer);
564
+ const account = encodeURIComponent(cognitoUser.getUsername());
565
+ const qrCodeUri = `otpauth://totp/${issuer}:${account}?secret=${secretCode}&issuer=${issuer}`;
566
+ resolve({ secretCode, qrCodeUri });
567
+ },
568
+ onFailure: (err) => reject(mapCognitoError(err))
569
+ });
570
+ });
571
+ }
572
+ /**
573
+ * Verifica il codice TOTP e completa il login. Dopo questa chiamata la
574
+ * sessione è autenticata e il challenge viene rimosso dalla mappa.
575
+ */
576
+ async verifyTotpChallenge(challengeSession, code) {
577
+ const cognitoUser = this._takeChallengeUser(challengeSession);
578
+ return new Promise((resolve, reject) => {
579
+ cognitoUser.verifySoftwareToken(code, this.config.totpIssuer, {
580
+ onSuccess: (session) => resolve(this._onAuthSuccess(cognitoUser, session)),
581
+ onFailure: (err) => reject(mapCognitoError(err))
582
+ });
583
+ });
584
+ }
585
+ // ─── Private helpers ───────────────────────────────────────────────────────
586
+ _getIdpClient() {
587
+ if (!this._idpClient) {
588
+ this._idpClient = new clientCognitoIdentityProvider.CognitoIdentityProviderClient({ region: this.config.region });
589
+ }
590
+ return this._idpClient;
591
+ }
592
+ _makeCognitoUser(username) {
593
+ return new amazonCognitoIdentityJs.CognitoUser({
594
+ Username: username,
595
+ Pool: this._pool,
596
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
597
+ Storage: this.config.storage
598
+ });
599
+ }
600
+ _storeChallengeUser(cognitoUser) {
601
+ const id = crypto.randomUUID();
602
+ this._pendingChallenges.set(id, cognitoUser);
603
+ return id;
604
+ }
605
+ _takeChallengeUser(challengeSession) {
606
+ const user = this._pendingChallenges.get(challengeSession);
607
+ if (!user) {
608
+ throw new CognitoAuthError(
609
+ "Challenge session non valida o scaduta",
610
+ "UNKNOWN"
611
+ );
612
+ }
613
+ this._pendingChallenges.delete(challengeSession);
614
+ return user;
615
+ }
616
+ /** Legge il CognitoUser senza rimuoverlo dalla mappa (per setupTotpChallenge). */
617
+ _peekChallengeUser(challengeSession) {
618
+ const user = this._pendingChallenges.get(challengeSession);
619
+ if (!user) {
620
+ throw new CognitoAuthError(
621
+ "Challenge session non valida o scaduta",
622
+ "UNKNOWN"
623
+ );
624
+ }
625
+ return user;
626
+ }
627
+ _onAuthSuccess(cognitoUser, session) {
628
+ const authSession = buildAuthSession(session);
629
+ const authUser = buildAuthUser(session, cognitoUser.getUsername());
630
+ this.setState("authenticated");
631
+ this.emit("signedIn", authUser);
632
+ this._scheduleRefresh(cognitoUser, session);
633
+ return { status: "SUCCESS", user: authUser, session: authSession };
634
+ }
635
+ _getRawSession(cognitoUser) {
636
+ return new Promise((resolve, reject) => {
637
+ cognitoUser.getSession((err, session) => {
638
+ if (err || !session) {
639
+ return reject(err ? mapCognitoError(err) : new SessionExpiredError());
640
+ }
641
+ if (!session.isValid()) {
642
+ return reject(new SessionExpiredError());
643
+ }
644
+ resolve(session);
645
+ });
646
+ });
647
+ }
648
+ async _getAuthenticatedUser() {
649
+ const cognitoUser = this._pool.getCurrentUser();
650
+ if (!cognitoUser) throw new SessionExpiredError();
651
+ await this._getRawSession(cognitoUser);
652
+ return cognitoUser;
653
+ }
654
+ _scheduleRefresh(cognitoUser, session) {
655
+ if (!this.config.autoRefresh) return;
656
+ this._clearRefreshTimer();
657
+ const expiresAt = session.getAccessToken().getExpiration() * 1e3;
658
+ const refreshIn = expiresAt - Date.now() - this.config.refreshMarginSeconds * 1e3;
659
+ if (refreshIn <= 0) return;
660
+ this._refreshTimer = setTimeout(() => {
661
+ const refreshToken = new amazonCognitoIdentityJs.CognitoRefreshToken({
662
+ RefreshToken: session.getRefreshToken().getToken()
663
+ });
664
+ cognitoUser.refreshSession(refreshToken, (err, newSession) => {
665
+ if (err) {
666
+ this.emit("sessionExpired");
667
+ this.setState("unauthenticated");
668
+ return;
669
+ }
670
+ this.emit("tokenRefreshed", buildAuthSession(newSession));
671
+ this._scheduleRefresh(cognitoUser, newSession);
672
+ });
673
+ }, refreshIn);
674
+ }
675
+ _clearRefreshTimer() {
676
+ if (this._refreshTimer !== null) {
677
+ clearTimeout(this._refreshTimer);
678
+ this._refreshTimer = null;
679
+ }
680
+ }
681
+ };
682
+
683
+ exports.AutoStorageAdapter = AutoStorageAdapter;
684
+ exports.CognitoAuthClient = CognitoAuthClient;
685
+ exports.CognitoAuthError = CognitoAuthError;
686
+ exports.InMemoryStorageAdapter = InMemoryStorageAdapter;
687
+ exports.InvalidCodeError = InvalidCodeError;
688
+ exports.LocalStorageAdapter = LocalStorageAdapter;
689
+ exports.NotAuthorizedError = NotAuthorizedError;
690
+ exports.SessionExpiredError = SessionExpiredError;
691
+ exports.SessionStorageAdapter = SessionStorageAdapter;
692
+ exports.TypedEventEmitter = TypedEventEmitter;
693
+ exports.UserNotConfirmedError = UserNotConfirmedError;
694
+ exports.mapCognitoError = mapCognitoError;
695
+ //# sourceMappingURL=index.cjs.map
696
+ //# sourceMappingURL=index.cjs.map