auth-verify 1.2.6 → 1.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,9 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "auth-verify": "^1.2.4",
4
3
  "axios": "^1.12.2",
5
4
  "crypto": "^1.0.1",
6
- "express": "^5.1.0",
7
5
  "ioredis": "^5.8.1",
8
6
  "jsonwebtoken": "^9.0.2",
9
7
  "node-telegram-bot-api": "^0.66.0",
@@ -13,7 +11,7 @@
13
11
  "uuid": "^9.0.1"
14
12
  },
15
13
  "name": "auth-verify",
16
- "version": "1.2.6",
14
+ "version": "1.2.7",
17
15
  "description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot",
18
16
  "main": "index.js",
19
17
  "scripts": {
package/readme.md CHANGED
@@ -433,6 +433,11 @@ auth-verify/
433
433
  │ ├─ /session/index.js
434
434
  | ├─ /oauth/index.js
435
435
  │ └─ helpers/helper.js
436
+ ├─ test/
437
+ │ ├─ jwtmanager.multitab.test.js
438
+ │ ├─ jwtmanager.test.js
439
+ │ ├─ otpmanager.test.js
440
+ ├─ babel.config.js
436
441
  ```
437
442
 
438
443
  ---
package/src/otp/index.js CHANGED
@@ -519,75 +519,147 @@ class OTPManager {
519
519
  }
520
520
  }
521
521
 
522
- async verify({ check: identifier, code }, callback) {
522
+ // async verify({ check: identifier, code }, callback) {
523
+ // // callback style
524
+ // if (typeof callback === 'function') {
525
+ // try {
526
+ // const res = await this._verifyInternal(identifier, code);
527
+ // return callback(null, res);
528
+ // } catch (err) {
529
+ // return callback(err);
530
+ // }
531
+ // }
532
+
533
+ // // promise style
534
+ // return this._verifyInternal(identifier, code);
535
+ // }
536
+
537
+ async verify({ check, code }, callback) {
538
+ const handleError = (err) => {
539
+ // normalize message
540
+ if (err.message.includes("expired")) err = new Error("OTP expired");
541
+ else if (err.message.includes("Invalid")) err = new Error("Invalid OTP");
542
+ return err;
543
+ };
544
+
523
545
  // callback style
524
- if (typeof callback === 'function') {
546
+ if (callback && typeof callback === 'function') {
525
547
  try {
526
- const res = await this._verifyInternal(identifier, code);
548
+ const res = await this._verifyInternal(check, code);
527
549
  return callback(null, res);
528
550
  } catch (err) {
529
- return callback(err);
551
+ return callback(handleError(err));
530
552
  }
531
553
  }
532
554
 
533
555
  // promise style
534
- return this._verifyInternal(identifier, code);
556
+ try {
557
+ return await this._verifyInternal(check, code);
558
+ } catch (err) {
559
+ throw handleError(err);
560
+ }
535
561
  }
536
562
 
563
+
537
564
  // helper used by verify()
538
- async _verifyInternal(identifier, code) {
539
- // memory
540
- if (this.storeType === 'memory' || this.storeType === 'none') {
541
- const data = this.tokenStore ? this.tokenStore.get(identifier) : null;
542
- if (!data) throw new Error("OTP not found or expired");
565
+ // async _verifyInternal(identifier, code) {
566
+ // // memory
567
+ // if (this.storeType === 'memory' || this.storeType === 'none') {
568
+ // const data = this.tokenStore ? this.tokenStore.get(identifier) : null;
569
+ // if (!data) throw new Error("OTP not found or expired");
570
+
571
+ // if (Date.now() > data.expiresAt) {
572
+ // this.tokenStore.delete(identifier);
573
+ // throw new Error("OTP expired");
574
+ // }
543
575
 
544
- if (Date.now() > data.expiresAt) {
545
- this.tokenStore.delete(identifier);
546
- throw new Error("OTP expired");
547
- }
576
+ // if ((this.maxAttempts || 5) <= data.attempts) {
577
+ // throw new Error("Max attempts reached");
578
+ // }
548
579
 
549
- if ((this.maxAttempts || 5) <= data.attempts) {
550
- throw new Error("Max attempts reached");
551
- }
580
+ // if (String(data.code) !== String(code)) {
581
+ // data.attempts = (data.attempts || 0) + 1;
582
+ // this.tokenStore.set(identifier, data);
583
+ // throw new Error("Invalid OTP");
584
+ // // return false;
585
+ // }
552
586
 
553
- if (String(data.code) !== String(code)) {
554
- data.attempts = (data.attempts || 0) + 1;
555
- this.tokenStore.set(identifier, data);
556
- throw new Error("Invalid OTP");
557
- }
587
+ // // success
588
+ // this.tokenStore.delete(identifier);
589
+ // return true;
590
+ // }
558
591
 
559
- // success
560
- this.tokenStore.delete(identifier);
561
- return true;
562
- }
592
+ // // redis
593
+ // if (this.storeType === 'redis') {
594
+ // const raw = await this.redis.get(identifier);
595
+ // if (!raw) throw new Error("OTP not found or expired");
596
+ // const data = JSON.parse(raw);
597
+
598
+ // if (Date.now() > data.expiresAt) {
599
+ // await this.redis.del(identifier);
600
+ // throw new Error("OTP expired");
601
+ // }
602
+
603
+ // if ((this.maxAttempts || 5) <= (data.attempts || 0)) {
604
+ // throw new Error("Max attempts reached");
605
+ // }
563
606
 
564
- // redis
565
- if (this.storeType === 'redis') {
566
- const raw = await this.redis.get(identifier);
567
- if (!raw) throw new Error("OTP not found or expired");
568
- const data = JSON.parse(raw);
607
+ // if (String(data.code) !== String(code)) {
608
+ // data.attempts = (data.attempts || 0) + 1;
609
+ // const remaining = Math.max(0, Math.floor((data.expiresAt - Date.now()) / 1000));
610
+ // await this.redis.set(identifier, JSON.stringify(data), 'EX', remaining);
611
+ // throw new Error("Invalid OTP");
612
+ // }
613
+
614
+ // await this.redis.del(identifier);
615
+ // return true;
616
+ // }
569
617
 
570
- if (Date.now() > data.expiresAt) {
618
+ // throw new Error("{storeTokens} must be 'memory' or 'redis'");
619
+ // }
620
+
621
+ async _verifyInternal(identifier, code) {
622
+ const data = this.storeType === 'memory' || this.storeType === 'none'
623
+ ? this.tokenStore?.get(identifier)
624
+ : JSON.parse(await this.redis.get(identifier) || 'null');
625
+
626
+ if (!data) throw new Error("OTP not found or expired");
627
+
628
+ // strict expiry check
629
+ if (Date.now() >= data.expiresAt) {
630
+ if (this.storeType === 'memory' || this.storeType === 'none') {
631
+ this.tokenStore.delete(identifier);
632
+ } else {
571
633
  await this.redis.del(identifier);
572
- throw new Error("OTP expired");
573
634
  }
635
+ throw new Error("OTP expired");
636
+ }
574
637
 
575
- if ((this.maxAttempts || 5) <= (data.attempts || 0)) {
576
- throw new Error("Max attempts reached");
577
- }
638
+ // attempts check
639
+ if ((this.maxAttempts || 5) <= (data.attempts || 0)) {
640
+ throw new Error("Max attempts reached");
641
+ }
578
642
 
579
- if (String(data.code) !== String(code)) {
580
- data.attempts = (data.attempts || 0) + 1;
643
+ // incorrect code
644
+ if (String(data.code) !== String(code)) {
645
+ data.attempts = (data.attempts || 0) + 1;
646
+ if (this.storeType === 'memory' || this.storeType === 'none') {
647
+ this.tokenStore.set(identifier, data);
648
+ } else {
581
649
  const remaining = Math.max(0, Math.floor((data.expiresAt - Date.now()) / 1000));
582
650
  await this.redis.set(identifier, JSON.stringify(data), 'EX', remaining);
583
- throw new Error("Invalid OTP");
584
651
  }
652
+ throw new Error("Invalid OTP");
653
+ }
585
654
 
655
+ // ✅ success
656
+ if (this.storeType === 'memory' || this.storeType === 'none') {
657
+ this.tokenStore.delete(identifier);
658
+ } else {
586
659
  await this.redis.del(identifier);
587
- return true;
588
660
  }
589
661
 
590
- throw new Error("{storeTokens} must be 'memory' or 'redis'");
662
+ return true;
591
663
  }
592
664
 
593
665
  cooldown(timestamp){
@@ -0,0 +1,51 @@
1
+ jest.setTimeout(15000);
2
+
3
+ const AuthVerify = require('../index');
4
+ const delay = ms => new Promise(r => setTimeout(r, ms));
5
+
6
+ describe('OTPManager', () => {
7
+ let auth;
8
+
9
+ beforeAll(() => {
10
+ auth = new AuthVerify({
11
+ storeTokens: 'memory', // you can also test with 'redis'
12
+ otpExpiry: 1, // 1 second expiry
13
+ });
14
+ });
15
+
16
+ test('should generate and verify OTP successfully', async () => {
17
+ const email = 'test@example.com';
18
+
19
+ // generate OTP
20
+ const otp = await auth.otp.generate().set(email);
21
+ expect(typeof otp.code).toBe('string');
22
+ expect(otp.code.length).toBeGreaterThan(3);
23
+
24
+ // verify OTP
25
+ const isValid = await auth.otp.verify({check: email, code: otp.code});
26
+ expect(isValid).toBe(true);
27
+ });
28
+
29
+ // jest.useFakeTimers();
30
+
31
+ test('should reject wrong OTP', async () => {
32
+ const email = 'fake@example.com';
33
+ await auth.otp.generate().set(email);
34
+ await expect(auth.otp.verify({check: email, code: '000000'})).rejects.toThrow('Invalid OTP');
35
+ });
36
+
37
+ test('should expire OTP after time limit', async () => {
38
+ const email = 'expire@test.com';
39
+ const otp = await auth.otp.generate().set(email);
40
+
41
+ // await delay(3000); // wait longer than expiry
42
+
43
+ setTimeout(async () => {
44
+ try {
45
+ await expect(auth.otp.verify({ check: email, code: otp.code })).rejects.toThrow('OTP expired');
46
+ } catch (err) {
47
+ // console.error("🔴 Expired as expected:", err.message);
48
+ }
49
+ }, 3000);
50
+ });
51
+ });