auth-verify 1.2.7 → 1.4.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
@@ -2,6 +2,7 @@ const JWTManager = require("./src/jwt");
2
2
  const OTPManager = require("./src/otp");
3
3
  const SessionManager = require("./src/session");
4
4
  const OAuthManager = require("./src/oauth");
5
+ const TOTPManager = require("./src/totp");
5
6
 
6
7
  class AuthVerify {
7
8
  constructor(options = {}) {
@@ -9,9 +10,15 @@ class AuthVerify {
9
10
  jwtSecret = "jwt_secret",
10
11
  cookieName = "jwt_token",
11
12
  otpExpiry = 300,
12
- storeTokens = "none",
13
+ storeTokens = "memory",
13
14
  otpHash = "sha256",
14
15
  redisUrl,
16
+ useAlg,
17
+ totp = {
18
+ digits: 6,
19
+ step: 30,
20
+ alg: "SHA1"
21
+ }
15
22
  } = options;
16
23
 
17
24
  // ✅ Ensure cookieName and secret always exist
@@ -22,6 +29,7 @@ class AuthVerify {
22
29
  this.jwt = new JWTManager(jwtSecret, {
23
30
  storeTokens,
24
31
  cookieName,
32
+ useAlg
25
33
  });
26
34
 
27
35
  this.otp = new OTPManager({
@@ -33,6 +41,7 @@ class AuthVerify {
33
41
 
34
42
  this.session = new SessionManager({ storeTokens, redisUrl });
35
43
  this.oauth = new OAuthManager();
44
+ this.totp = new TOTPManager(totp);
36
45
 
37
46
  this.senders = new Map();
38
47
  }
package/package.json CHANGED
@@ -6,13 +6,14 @@
6
6
  "jsonwebtoken": "^9.0.2",
7
7
  "node-telegram-bot-api": "^0.66.0",
8
8
  "nodemailer": "^7.0.6",
9
+ "qrcode": "^1.5.4",
9
10
  "redis": "^5.8.3",
10
11
  "twilio": "^5.10.3",
11
12
  "uuid": "^9.0.1"
12
13
  },
13
14
  "name": "auth-verify",
14
- "version": "1.2.7",
15
- "description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot",
15
+ "version": "1.4.0",
16
+ "description": "A simple Node.js library for sending and verifying OTP via email, SMS and Telegram bot. And handling JWT with Cookies",
16
17
  "main": "index.js",
17
18
  "scripts": {
18
19
  "test": "jest --runInBand"
@@ -39,7 +40,9 @@
39
40
  "jwt",
40
41
  "oauth",
41
42
  "redis",
42
- "cookie"
43
+ "cookie",
44
+ "jwa",
45
+ "jsonwebtoken"
43
46
  ],
44
47
  "author": "Jahongir Sobirov",
45
48
  "license": "MIT",
package/readme.md CHANGED
@@ -3,6 +3,7 @@
3
3
  **auth-verify** is a Node.js authentication utility that provides:
4
4
  - ✅ Secure OTP (one-time password) generation and verification
5
5
  - ✅ Sending OTPs via Email, SMS (pluggable helpers), and Telegram bot
6
+ - ✅ TOTP (Time-based One Time Passwords) generation code and QR code and verification (Google Authenticator support)
6
7
  - ✅ JWT creation, verification and optional token revocation with memory/Redis storage
7
8
  - ✅ Session management (in-memory or Redis)
8
9
  - ✅ New: OAuth 2.0 integration for Google, Facebook, GitHub, X (Twitter) and Linkedin
@@ -48,6 +49,20 @@ const auth = new AuthVerify({
48
49
 
49
50
  ## 🔐 JWT Usage
50
51
 
52
+ ### JWA Handling (v1.3.0+)
53
+
54
+ You can choose json web algorithm for signing jwt
55
+ ```js
56
+ const AuthVerify = require('auth-verify');
57
+ const auth = new AuthVerify({ useAlg: 'HS512' }); // or 'HS256'
58
+
59
+ (async ()=>{
60
+ const token = await auth.jwt.sign({userId: 123}, '30m');
61
+ console.log('token', token);
62
+ })();
63
+ ```
64
+
65
+
51
66
  ```js
52
67
  // create JWT
53
68
  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 +217,77 @@ auth.otp.verify({ check: 'user@example.com', code: '123456' }, (err, isValid)=>{
202
217
  `resend` returns the new code (promise style) or calls callback.
203
218
 
204
219
  ---
205
- ## 🌍 OAuth 2.0 Integration (New in v1.2.0)
220
+
221
+ ## ✅ TOTP (Time-based One Time Passwords) — Google Authenticator support (New in v1.4.0)
222
+ ```js
223
+ const AuthVerify = require("auth-verify");
224
+ const auth = new AuthVerify();
225
+ // Optionally:
226
+ /*
227
+ const AuthVerify = require("auth-verify");
228
+ const auth = new AuthVerify({
229
+ totp: {
230
+ digits: 6 (default)
231
+ step: 30 (default)
232
+ alg: "SHA1" (default)
233
+ }
234
+ });
235
+ */
236
+ ```
237
+ You can change `digits`, `step`, `alg`.
238
+ - `digits`: how many digits your one-time password has **(Google Authenticator default = 6 digits)**
239
+ - `step`: how long each TOTP code lives in seconds **(Google Authenticator default = 30 seconds)**
240
+ - `alg`: the hashing algorithm used to generate the OTP **(Google Authenticator default = SHA1)**
241
+ ### Generate secret
242
+ ```js
243
+ const secret = auth.totp.secret();
244
+ console.log(secret); //base 32
245
+ ```
246
+ ### generate otpauth URI
247
+ ```js
248
+ const uri = auth.totp.uri({
249
+ label: "user@example.com",
250
+ issuer: "AuthVerify",
251
+ secret
252
+ });
253
+
254
+ console.log(uri);
255
+ ```
256
+ ### generate QR code image
257
+ (send this PNG to frontend or show in UI)
258
+ ```js
259
+ const qr = await auth.totp.qrcode(uri);
260
+ console.log(qr); // data:image/png;base64,...
261
+ ```
262
+ ### generate a TOTP code
263
+ ```js
264
+ const token = auth.totp.generate(secret);
265
+ console.log("TOTP:", token);
266
+ ```
267
+ ### verify a code entered by user
268
+ ```js
269
+ const ok = auth.totp.verify({ secret, token });
270
+ console.log(ok); // true or false
271
+ ```
272
+ ### example real flow
273
+ ```js
274
+ // Register UI
275
+ const secret = auth.totp.secret();
276
+ const uri = auth.totp.uri({ label: "john@example.com", issuer: "AuthVerify", secret });
277
+ const qr = await auth.totp.qrcode(uri);
278
+ // show qr to user
279
+
280
+ // Then user scans QR with Google Authenticator
281
+ // Then user enters 6-digit code
282
+ const token = req.body.code;
283
+
284
+ // Verify
285
+ if (auth.totp.verify({ secret, token })) {
286
+ // enable 2FA
287
+ }
288
+ ```
289
+ ---
290
+ ## 🌍 OAuth 2.0 Integration (v1.2.0+)
206
291
  `auth.oauth` supports login via Google, Facebook, GitHub, X (Twitter) and Linkedin.
207
292
  ### Example (Google Login with Express)
208
293
  ```js
@@ -333,7 +418,7 @@ app.get("/auth/linkedin/callback", async (req, res)=>{
333
418
  try{
334
419
  const { code } = req.query;
335
420
  const user = await linkedin.callback(code);
336
- res.json({ success: true, provider: "x", user });
421
+ res.json({ success: true, provider: "linkedin", user });
337
422
  }catch(err){
338
423
  res.status(400).json({ error: err.message });
339
424
  }
@@ -368,7 +453,7 @@ auth.register.sender('consoleOtp', async ({ to, code }) => {
368
453
  });
369
454
 
370
455
  // use it later (chainable)
371
- await auth.use('consoleOtp').send({ to: '+998901234567', code: await auth.otp.generate(5) });
456
+ await auth.use('consoleOtp').send({ to: '+998901234567', code: await auth.otp.generate(5).code });
372
457
  ```
373
458
 
374
459
  ---
@@ -424,19 +509,24 @@ Notes:
424
509
  auth-verify/
425
510
  ├─ README.md
426
511
  ├─ package.json
512
+ ├─ index.js // exports AuthVerify
427
513
  ├─ src/
428
- │ ├─ index.js // exports AuthVerify
429
514
  │ ├─ jwt/
430
515
  | | ├─ index.js
431
516
  | | ├─ cookie/index.js
432
517
  │ ├─ /otp/index.js
518
+ │ ├─ totp/
519
+ | | ├─ index.js
520
+ | | ├─ base32.js
433
521
  │ ├─ /session/index.js
434
522
  | ├─ /oauth/index.js
435
523
  │ └─ helpers/helper.js
436
524
  ├─ test/
525
+ │ ├─ jwa.test.js
437
526
  │ ├─ jwtmanager.multitab.test.js
438
527
  │ ├─ jwtmanager.test.js
439
528
  │ ├─ otpmanager.test.js
529
+ │ ├─ totpmanager.test.js
440
530
  ├─ babel.config.js
441
531
  ```
442
532
 
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
  });
@@ -0,0 +1,46 @@
1
+ // base32.js
2
+ const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
3
+
4
+ function encode(buffer) {
5
+ let bits = 0;
6
+ let value = 0;
7
+ let output = "";
8
+
9
+ for (let i = 0; i < buffer.length; i++) {
10
+ value = (value << 8) | buffer[i];
11
+ bits += 8;
12
+
13
+ while (bits >= 5) {
14
+ output += alphabet[(value >>> (bits - 5)) & 31];
15
+ bits -= 5;
16
+ }
17
+ }
18
+
19
+ if (bits > 0) {
20
+ output += alphabet[(value << (5 - bits)) & 31];
21
+ }
22
+ return output;
23
+ }
24
+
25
+ function decode(str) {
26
+ let bits = 0;
27
+ let value = 0;
28
+ const output = [];
29
+
30
+ for (const char of str.toUpperCase()) {
31
+ if (char === "=") break;
32
+ const idx = alphabet.indexOf(char);
33
+ if (idx === -1) continue;
34
+
35
+ value = (value << 5) | idx;
36
+ bits += 5;
37
+
38
+ if (bits >= 8) {
39
+ output.push((value >>> (bits - 8)) & 0xff);
40
+ bits -= 8;
41
+ }
42
+ }
43
+ return Buffer.from(output);
44
+ }
45
+
46
+ module.exports = { encode, decode };
@@ -0,0 +1,66 @@
1
+ // totp.js
2
+ const crypto = require("crypto");
3
+ const base32 = require("./base32");
4
+
5
+ class TOTPManager {
6
+ constructor(options = {}) {
7
+ this.step = options.step || 30;
8
+ this.digits = options.digits || 6;
9
+ this.algorithm = (options.alg || "SHA1").toUpperCase();
10
+ }
11
+
12
+ // generate random Base32 secret
13
+ secret(length = 20) {
14
+ const buf = crypto.randomBytes(length);
15
+ return base32.encode(buf);
16
+ }
17
+
18
+ // generate TOTP code
19
+ generate(secret) {
20
+ const time = Math.floor(Date.now() / 1000 / this.step);
21
+ return this._hotp(secret, time);
22
+ }
23
+
24
+ // verify code from user
25
+ verify(secret, code, window = 1) {
26
+ // check ± 1 window to allow delay
27
+ const time = Math.floor(Date.now() / 1000 / this.step);
28
+ for (let i = -window; i <= window; i++) {
29
+ const otp = this._hotp(secret, time + i);
30
+ if (otp === code) return true;
31
+ }
32
+ return false;
33
+ }
34
+
35
+ // build key URI for authenticator apps
36
+ uri({ label, secret, issuer }) {
37
+ return `otpauth://totp/${encodeURIComponent(issuer)}:${encodeURIComponent(label)}?secret=${secret}&issuer=${encodeURIComponent(issuer)}&algorithm=${this.algorithm}&digits=${this.digits}&period=${this.step}`;
38
+ }
39
+
40
+ // optional QR for CLI users (we can integrate QR lib later)
41
+ async qrcode(uri) {
42
+ const qrcode = require("qrcode");
43
+ return await qrcode.toDataURL(uri);
44
+ }
45
+
46
+ // internal HOTP algorithm
47
+ _hotp(secret, counter) {
48
+ const key = base32.decode(secret);
49
+
50
+ const buf = Buffer.alloc(8);
51
+ buf.writeUInt32BE(Math.floor(counter / Math.pow(2, 32)), 0);
52
+ buf.writeUInt32BE(counter & 0xffffffff, 4);
53
+
54
+ const hmac = crypto.createHmac(this.algorithm, key).update(buf).digest();
55
+ const offset = hmac[hmac.length - 1] & 0xf;
56
+
57
+ const code = ((hmac[offset] & 0x7f) << 24) |
58
+ ((hmac[offset + 1] & 0xff) << 16) |
59
+ ((hmac[offset + 2] & 0xff) << 8) |
60
+ (hmac[offset + 3] & 0xff);
61
+
62
+ return (code % 10 ** this.digits).toString().padStart(this.digits, "0");
63
+ }
64
+ }
65
+
66
+ module.exports = TOTPManager;
@@ -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,54 @@
1
+ const AuthVerify = require('../index');
2
+
3
+ describe("TOTP Manager", () => {
4
+ let auth;
5
+ let secret;
6
+
7
+ beforeAll(() => {
8
+ auth = new AuthVerify();
9
+ secret = auth.totp.secret();
10
+ });
11
+
12
+ test("should generate base32 secret", () => {
13
+ expect(typeof secret).toBe("string");
14
+ expect(secret.length).toBeGreaterThan(10);
15
+ });
16
+
17
+ test("should generate 6 digit code", () => {
18
+ const code = auth.totp.generate(secret);
19
+ expect(code).toMatch(/^[0-9]{6}$/);
20
+ });
21
+
22
+ test("should verify valid totp", () => {
23
+ const code = auth.totp.generate(secret);
24
+ expect(auth.totp.verify(secret, code)).toBe(true);
25
+ });
26
+
27
+ test("should reject invalid totp", () => {
28
+ const fake = "000000";
29
+ expect(auth.totp.verify(secret, fake)).toBe(false);
30
+ });
31
+
32
+ test("should generate valid otpauth:// URL", () => {
33
+ const uri = auth.totp.uri({
34
+ label: "test@example.com",
35
+ issuer: "AuthVerify",
36
+ secret,
37
+ });
38
+
39
+ expect(uri.startsWith("otpauth://totp/")).toBe(true);
40
+ expect(uri.includes("secret=")).toBe(true);
41
+ });
42
+
43
+ test("should generate QR DataURL", async () => {
44
+ const uri = auth.totp.uri({
45
+ label: "qrtest@example.com",
46
+ issuer: "AuthVerify",
47
+ secret,
48
+ });
49
+
50
+ const url = await auth.totp.qrcode(uri);
51
+
52
+ expect(url.startsWith("data:image/png;base64,")).toBe(true);
53
+ });
54
+ });