auth-verify 1.2.6 → 1.3.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.
package/index.js CHANGED
@@ -9,9 +9,10 @@ class AuthVerify {
9
9
  jwtSecret = "jwt_secret",
10
10
  cookieName = "jwt_token",
11
11
  otpExpiry = 300,
12
- storeTokens = "none",
12
+ storeTokens = "memory",
13
13
  otpHash = "sha256",
14
14
  redisUrl,
15
+ useAlg
15
16
  } = options;
16
17
 
17
18
  // ✅ Ensure cookieName and secret always exist
@@ -22,6 +23,7 @@ class AuthVerify {
22
23
  this.jwt = new JWTManager(jwtSecret, {
23
24
  storeTokens,
24
25
  cookieName,
26
+ useAlg
25
27
  });
26
28
 
27
29
  this.otp = new OTPManager({
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,8 +11,8 @@
13
11
  "uuid": "^9.0.1"
14
12
  },
15
13
  "name": "auth-verify",
16
- "version": "1.2.6",
17
- "description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot",
14
+ "version": "1.3.0",
15
+ "description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot. And handling JWT with Cookies",
18
16
  "main": "index.js",
19
17
  "scripts": {
20
18
  "test": "jest --runInBand"
@@ -41,7 +39,9 @@
41
39
  "jwt",
42
40
  "oauth",
43
41
  "redis",
44
- "cookie"
42
+ "cookie",
43
+ "jwa",
44
+ "jsonwebtoken"
45
45
  ],
46
46
  "author": "Jahongir Sobirov",
47
47
  "license": "MIT",
package/readme.md CHANGED
@@ -48,6 +48,20 @@ const auth = new AuthVerify({
48
48
 
49
49
  ## 🔐 JWT Usage
50
50
 
51
+ ### JWA Handling (New in v1.3.0)
52
+
53
+ You can choose json web algorithm for signing jwt
54
+ ```js
55
+ const AuthVerify = require('auth-verify');
56
+ const auth = new AuthVerify({ useAlg: 'HS512' }); // or 'HS256'
57
+
58
+ (async ()=>{
59
+ const token = await auth.jwt.sign({userId: 123}, '30m');
60
+ console.log('token', token);
61
+ })();
62
+ ```
63
+
64
+
51
65
  ```js
52
66
  // create JWT
53
67
  const token = await auth.jwt.sign({ userId: 123 }, '1h'); // expiry string or number (ms) (and also you can add '1m' (minute), '5s' (second) and '7d' (day))
@@ -202,7 +216,7 @@ auth.otp.verify({ check: 'user@example.com', code: '123456' }, (err, isValid)=>{
202
216
  `resend` returns the new code (promise style) or calls callback.
203
217
 
204
218
  ---
205
- ## 🌍 OAuth 2.0 Integration (New in v1.2.0)
219
+ ## 🌍 OAuth 2.0 Integration (v1.2.0+)
206
220
  `auth.oauth` supports login via Google, Facebook, GitHub, X (Twitter) and Linkedin.
207
221
  ### Example (Google Login with Express)
208
222
  ```js
@@ -368,7 +382,7 @@ auth.register.sender('consoleOtp', async ({ to, code }) => {
368
382
  });
369
383
 
370
384
  // use it later (chainable)
371
- await auth.use('consoleOtp').send({ to: '+998901234567', code: await auth.otp.generate(5) });
385
+ await auth.use('consoleOtp').send({ to: '+998901234567', code: await auth.otp.generate(5).code });
372
386
  ```
373
387
 
374
388
  ---
@@ -433,6 +447,12 @@ auth-verify/
433
447
  │ ├─ /session/index.js
434
448
  | ├─ /oauth/index.js
435
449
  │ └─ helpers/helper.js
450
+ ├─ test/
451
+ │ ├─ jwa.test.js
452
+ │ ├─ jwtmanager.multitab.test.js
453
+ │ ├─ jwtmanager.test.js
454
+ │ ├─ otpmanager.test.js
455
+ ├─ babel.config.js
436
456
  ```
437
457
 
438
458
  ---
package/src/jwt/index.js CHANGED
@@ -251,7 +251,8 @@ class JWTManager {
251
251
  // if (!secret) throw new Error("JWT secret is required");
252
252
  this.secret = secret || "jwt_secret";
253
253
  this.cookieName = options.cookieName || "jwt_token";
254
- this.storeType = options.storeTokens || "none";
254
+ this.storeType = options.storeTokens || "memory";
255
+ this.jwtAlgorithm = options.useAlg || "HS256";
255
256
 
256
257
  if (this.storeType === "memory") {
257
258
  this.tokenStore = new Map();
@@ -290,8 +291,9 @@ class JWTManager {
290
291
 
291
292
  const createToken = () =>
292
293
  new Promise((resolve, reject) => {
293
- jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, token) => {
294
+ jwt.sign(payload, this.secret, { expiresIn: expiryJwt, algorithm: this.jwtAlgorithm}, (err, token) => {
294
295
  if (err) return reject(err);
296
+ // console.log(this.jwtAlgorithm);
295
297
  resolve(token);
296
298
  });
297
299
  });
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,45 @@
1
+ const AuthVerify = require('../index');
2
+
3
+ describe('JWA / JWT tests', () => {
4
+
5
+ let auth;
6
+
7
+ beforeAll(() => {
8
+ auth = new AuthVerify({
9
+ useAlg: 'HS512'
10
+ });
11
+ });
12
+
13
+ test('should sign token with HS512', async () => {
14
+ const token = await auth.jwt.sign({ id: 1 }, '1h');
15
+
16
+ expect(typeof token).toBe('string');
17
+
18
+ // decode headers — NOT verify
19
+ const parts = token.split('.');
20
+ const header = JSON.parse(Buffer.from(parts[0], 'base64url').toString());
21
+
22
+ expect(header.alg).toBe('HS512');
23
+ });
24
+
25
+ test('should verify token payload', async () => {
26
+ const token = await auth.jwt.sign({ id: 123 }, '1h');
27
+
28
+ const payload = await auth.jwt.verify(token);
29
+
30
+ expect(payload.id).toBe(123);
31
+ });
32
+
33
+ test('should reject tampered token', async () => {
34
+ const token = await auth.jwt.sign({ id: 1 }, '1h');
35
+
36
+ // break signature
37
+ const parts = token.split('.');
38
+ const bad = parts[0] + '.' + parts[1] + '.xxxx';
39
+
40
+ await expect(auth.jwt.verify(bad))
41
+ .rejects
42
+ .toThrow();
43
+ });
44
+
45
+ });
@@ -7,7 +7,7 @@ describe('JWTManager', () => {
7
7
  let auth;
8
8
 
9
9
  beforeAll(() => {
10
- auth = new AuthVerify({jwtSecret: 'test_secret', storeTokens: 'memory'});
10
+ auth = new AuthVerify({jwtSecret: 'test_secret', storeTokens: 'memory', useAlg: "HS512"});
11
11
  });
12
12
 
13
13
  test('should sign and verify a JWT', async () => {
@@ -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
+ });