auth-verify 0.0.1 → 1.0.3

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
@@ -1,182 +1,86 @@
1
- const sqlite3 = require("sqlite3").verbose();
2
- const nodemailer = require('nodemailer')
3
- const crypto = require('crypto');
4
- const { request } = require("http");
5
-
6
- class Verifier {
7
- constructor(sender = {}){
8
- this.otpLen = sender.otp?.leng || 6;
9
- this.expMin = sender.otp?.expMin || 3;
10
- this.limit = sender.otp?.limit || 5;
11
- this.cooldown = sender.otp?.cooldown || 60;
12
- // Setup SMTP
13
- this.transport = nodemailer.createTransport({
14
- service: `${sender.serv}`,
15
- auth: {
16
- user: `${sender.sender}`,
17
- pass: sender.pass
1
+ const JWTManager = require("./src/jwt");
2
+ const OTPManager = require("./src/otp");
3
+ const SessionManager = require("./src/session");
4
+
5
+ class AuthVerify {
6
+ constructor(options = {}) {
7
+ const {
8
+ jwtSecret,
9
+ otpExpiry = 300,
10
+ storeTokens = "none",
11
+ otpHash = "sha256",
12
+ redisUrl,
13
+ } = options;
14
+
15
+ if (!jwtSecret) throw new Error("jwtSecret is required in AuthVerify options");
16
+
17
+ this.senderName;
18
+ this.jwt = new JWTManager(jwtSecret, { storeTokens });
19
+ this.otp = new OTPManager({
20
+ storeTokens,
21
+ otpExpiry,
22
+ otpHash,
23
+ redisUrl,
24
+ });
25
+ this.session = new SessionManager({ storeTokens, redisUrl });
26
+
27
+ this.senders = new Map();
28
+ // this.register = {
29
+ // sender: (name, fn)=>{
30
+ // if (!name || typeof fn !== "function") {
31
+ // throw new Error("Sender registration requires a name and a function");
32
+ // }else{
33
+ // try{
34
+ // this.senders.set(name, fn);
35
+ // }catch(err){
36
+ // throw new Error(err);
37
+ // }
38
+ // }
39
+ // }
40
+ // }
41
+ // ✅ No getters — directly reference otp.dev (it's a plain object)
42
+ }
43
+
44
+ // Session helpers
45
+ async createSession(userId, options = {}) {
46
+ return this.session.create(userId, options);
47
+ }
48
+
49
+ async verifySession(sessionId) {
50
+ return this.session.verify(sessionId);
51
+ }
52
+
53
+ async destroySession(sessionId) {
54
+ return this.session.destroy(sessionId);
55
+ }
56
+
57
+ async use(name){
58
+ const senderFn = this.senders.get(name);
59
+ if(!senderFn) throw new Error(`Sender "${name}" not found`);
60
+ this.senderName = senderFn;
61
+ }
62
+
63
+ register = {
64
+ sender: (name, fn) => {
65
+ if (!name || typeof fn !== "function") {
66
+ throw new Error("Sender registration requires a name and a function");
18
67
  }
19
- });
20
-
21
- this.sender = `${sender.smtp?.sender}`
22
-
23
- //making a db for saving otp codes
24
- this.db = new sqlite3.Database('./authverify.db');
25
- this.db.serialize(()=>{
26
- // creating a table
27
- this.db.run(`
28
- CREATE TABLE IF NOT EXISTS verifications (
29
- email TEXT PRIMARY KEY,
30
- code TEXT,
31
- expiresAt TEXT,
32
- requests INTEGER DEFAULT 0,
33
- lastRequest TEXT
34
- )
35
- `);
36
- });
37
-
38
- }
39
-
40
- // Generate OTP securely
41
- generateOTP() {
42
- const buffer = crypto.randomBytes(this.otpLen);
43
- const number = parseInt(buffer.toString('hex'), 16)
44
- .toString()
45
- .slice(0, this.otpLen);
46
- return number.padStart(this.otpLen, '0');
47
- }
48
-
49
- html(html){
50
- this.htmlContent = html;
51
- return this;
52
- }
53
-
54
- subject(subject){
55
- this.mailSubject = subject;
56
- return this;
57
- }
58
-
59
- text(text){
60
- this.mailText = text;
61
- return this;
62
- }
63
-
64
- sendTo(email, callback){
65
-
66
- this.recieverEmail = email;
67
- const code = this.generateOTP();
68
- const expAt = new Date(Date.now() + this.expMin * 60 * 1000).toISOString();
69
-
70
- this.db.get(`SELECT * FROM verifications WHERE email = ?`, [email], (err, row)=>{
71
- if(err) return callback(err);
72
-
73
- const now = new Date();
74
- let requests = 1;
75
-
76
- if(row){
77
- const lastRequest = row.lastRequest ? new Date(row.lastRequest) : null;
78
-
79
- if(lastRequest && now.toDateString() === lastRequest.toDateString()){
80
- requests = row.requests + 1;
81
- if(requests > this.limit){
82
- return callback(new Error("Too many OTP requests today"));
83
- }
84
-
85
- const diff = (now - lastRequest) / 1000;
86
- if(diff < this.cooldown){
87
- return callback(new Error(`Please wait ${Math.ceil(this.cooldown - diff)} seconds before requesting a new OTP.`));
88
- }
89
- }
90
- }
91
-
92
- this.db.run(`INSERT INTO verifications (email, code, expiresAt, requests, lastRequest) VALUES (?, ?, ?, ?, ?)
93
- ON CONFLICT(email) DO UPDATE SET
94
- code = excluded.code,
95
- expiresAt = excluded.expiresAt,
96
- requests = excluded.requests,
97
- lastRequest = excluded.lastRequest`, [email, code, expAt, requests, now.toISOString()], (DBerr)=>{
98
- if(DBerr) return callback(DBerr);
99
-
100
- this.otpOptions = {
101
- from: this.sender,
102
- to: `${email}`,
103
- subject: this.mailSubject ? this.mailSubject.replace("{otp}", code) : undefined,
104
- text: this.mailText ? this.mailText.replace("{otp}", code) : undefined,
105
- html: this.htmlContent ? this.htmlContent.replace("{otp}", code) : undefined
106
- }
107
-
108
- this.transport.sendMail(this.otpOptions, (emailErr, info)=>{
109
- if(emailErr) return callback(emailErr);
110
-
111
- // console.log('📨 Email sent:', info.response);
112
- callback(null, true);
113
- });
114
-
115
- // console.log('💾 Code saved to DB');
116
- });
117
- });
118
-
119
- return this;
120
-
121
- }
122
-
123
- code(userCode){
124
- this.userCode = userCode
125
- return this;
126
- }
127
-
128
- verifyFor(email, callback){
129
- const now = new Date().toISOString();
130
-
131
- this.db.get(`SELECT * FROM verifications WHERE email = ? AND expiresAt > ?`, [email, now], (DBerr, row)=>{
132
- if(DBerr) return callback(DBerr);
68
+ this.senders.set(name, fn);
69
+ // console.log(`✅ Sender registered: ${name}`);
70
+ }
71
+ };
133
72
 
134
- // console.log(`Code was got`);
73
+ // use a sender by name
74
+ use(name) {
75
+ const senderFn = this.senders.get(name);
76
+ if (!senderFn) throw new Error(`Sender "${name}" not found`);
135
77
 
136
- if(!row){
137
- // console.log(this.recieverEmail);
138
- callback(null, false)
139
- }else{
140
- if(row.code === this.userCode){
141
- // console.log('✅ User verified');
142
- callback(null, true);
143
- }else{
144
- // console.log("❌ Code isn't correct")
145
- callback(null, false);
146
- }
78
+ return {
79
+ send: async (options) => {
80
+ return await senderFn(options); // call user function
147
81
  }
148
- });
149
- }
150
-
151
- makeOTP(length){
152
- const buffer = crypto.randomBytes(length);
153
- const number = parseInt(buffer.toString('hex'), 16)
154
- .toString()
155
- .slice(0, length);
156
- return number.padStart(length, '0');
157
- }
158
-
159
- getOTP(email, callback){
160
- this.db.get(`SELECT * FROM verifications WHERE email = ?`, [email], (err, row)=>{
161
- if(err) return callback(err);
162
- if(!row) return callback(null, null);
163
-
164
- callback(null, { code: row.code, expiresAt: row.expiresAt });
165
- });
82
+ };
166
83
  }
167
-
168
- cleanExpired() {
169
- const now = new Date().toISOString();
170
- this.db.run(
171
- `DELETE FROM verifications WHERE expiresAt <= ?`,
172
- [now],
173
- function(err) {
174
- if (err) console.error("Error cleaning expired OTPs:", err.message);
175
- // Optional: console.log(`${this.changes} expired OTPs removed`);
176
- }
177
- );
178
- }
179
-
180
84
  }
181
85
 
182
- module.exports = Verifier;
86
+ module.exports = AuthVerify;
package/package.json CHANGED
@@ -1,15 +1,19 @@
1
1
  {
2
2
  "dependencies": {
3
+ "axios": "^1.12.2",
3
4
  "crypto": "^1.0.1",
4
- "express": "^5.1.0",
5
+ "ioredis": "^5.8.1",
6
+ "jsonwebtoken": "^9.0.2",
7
+ "node-telegram-bot-api": "^0.66.0",
5
8
  "nodemailer": "^7.0.6",
6
- "sqlite3": "^5.1.7"
9
+ "redis": "^5.8.3",
10
+ "twilio": "^5.10.3",
11
+ "uuid": "^13.0.0"
7
12
  },
8
13
  "name": "auth-verify",
9
- "version": "0.0.1",
14
+ "version": "1.0.3",
10
15
  "description": "A simple Node.js library for sending and verifying OTP via email",
11
16
  "main": "index.js",
12
- "devDependencies": {},
13
17
  "scripts": {
14
18
  "test": "node test.js"
15
19
  },
@@ -31,12 +35,14 @@
31
35
  "login",
32
36
  "two-factor",
33
37
  "2fa",
34
- "email"
38
+ "email",
39
+ "jwt"
35
40
  ],
36
41
  "author": "Jahongir Sobirov",
37
42
  "license": "MIT",
38
43
  "bugs": {
39
44
  "url": "https://github.com/jahongir2007/auth-verify/issues"
40
45
  },
41
- "homepage": "https://jahongir2007.github.io/auth-verify/"
46
+ "homepage": "https://jahongir2007.github.io/auth-verify/",
47
+ "devDependencies": {}
42
48
  }
package/readme.md ADDED
@@ -0,0 +1,262 @@
1
+ # auth-verify
2
+
3
+ **auth-verify** is a Node.js authentication utility that provides:
4
+ - Secure OTP (one-time password) generation and verification
5
+ - Sending OTPs via Email, SMS (pluggable helpers), and Telegram bot
6
+ - JWT creation, verification and optional token revocation with memory/Redis storage
7
+ - Session management (in-memory or Redis)
8
+ - Developer extensibility: custom senders and `auth.register.sender()` / `auth.use(name).send(...)`
9
+
10
+ > This README documents the code structure and APIs found in the library files you provided (OTPManager, JWTManager, SessionManager, AuthVerify).
11
+
12
+ ---
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # from npm (when published)
18
+ npm install auth-verify
19
+
20
+ # or locally during development
21
+ # copy the package into your project and `require` it`
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Quick overview
27
+
28
+ - `AuthVerify` (entry): constructs and exposes `.jwt`, `.otp`, and (optionally) `.session` managers.
29
+ - `JWTManager`: sign, verify, decode, revoke tokens. Supports `storeTokens: "memory" | "redis" | "none"`.
30
+ - `OTPManager`: generate, store, send, verify, resend OTPs. Supports `storeTokens: "memory" | "redis" | "none"`. Supports email, SMS helper, Telegram bot, and custom dev senders.
31
+ - `SessionManager`: simple session creation/verification/destroy with memory or Redis backend.
32
+
33
+ ---
34
+
35
+ ## Example: Initialize library (CommonJS)
36
+
37
+ ```js
38
+ const AuthVerify = require('auth-verify');
39
+
40
+ const auth = new AuthVerify({
41
+ jwtSecret: "super_secret_value",
42
+ storeTokens: "memory", // or "redis" or "none"
43
+ otpExpiry: "5m", // supports number (seconds) OR string like '30s', '5m', '1h'
44
+ otpHash: "sha256", // optional OTP hashing algorithm
45
+ // you may pass redisUrl inside managers' own options when using redis
46
+ });
47
+ ```
48
+
49
+ ---
50
+
51
+ ## JWT usage
52
+
53
+ ```js
54
+ // create JWT
55
+ const token = await auth.jwt.sign({ userId: 123 }, '1h'); // expiry string or number (ms)
56
+ console.log('token', token);
57
+
58
+ // verify
59
+ const decoded = await auth.jwt.verify(token);
60
+ console.log('decoded', decoded);
61
+
62
+ // decode without verification
63
+ const payload = await auth.jwt.decode(token);
64
+
65
+ // revoke a token (immediately)
66
+ await auth.jwt.revoke(token, 0);
67
+
68
+ // revoke token until a time in the future (e.g. '10m' or number ms)
69
+ await auth.jwt.revokeUntil(token, '10m');
70
+
71
+ // check if token is revoked (returns boolean)
72
+ const isRevoked = await auth.jwt.isRevoked(token);
73
+ ```
74
+
75
+ Notes:
76
+ - `sign` and `verify` support callback and promise styles in the implementation. When `storeTokens` is `"redis"` you should use the promise/async style (callback mode returns an error for redis in the current implementation).
77
+
78
+ ---
79
+
80
+ ## OTP (email / sms / telegram / custom sender)
81
+
82
+ ### Configure sender
83
+
84
+ You can set the default sender (email/sms/telegram):
85
+
86
+ ```js
87
+ // email example
88
+ auth.otp.setSender({
89
+ via: 'email',
90
+ sender: 'your@address.com',
91
+ pass: 'app-password-or-smtp-pass',
92
+ service: 'gmail' // or 'smtp'
93
+ // if smtp service: host, port, secure (boolean)
94
+ });
95
+
96
+ // sms example (the internal helper expects provider/apiKey or mock flag)
97
+ auth.otp.setSender({
98
+ via: 'sms',
99
+ provider: 'infobip',
100
+ apiKey: 'xxx',
101
+ apiSecret: 'yyy',
102
+ sender: 'SENDER_NAME',
103
+ mock: true // in dev prints message instead of sending
104
+ });
105
+
106
+ // telegram example
107
+ auth.otp.setSender({
108
+ via: 'telegram',
109
+ token: '123:ABC', // bot token
110
+ // call auth.otp.setupTelegramBot(token) to start the bot
111
+ });
112
+ ```
113
+
114
+ ### Generate → Save → Send (chainable)
115
+
116
+ OTP generation is chainable: `generate()` returns the OTP manager instance.
117
+
118
+ ```js
119
+ // chainable + callback style example
120
+ auth.otp.generate(6).set('user@example.com', (err) => {
121
+ if (err) throw err;
122
+ auth.otp.message({
123
+ to: 'user@example.com',
124
+ subject: 'Your OTP',
125
+ html: `Your code: <b>${auth.otp.code}</b>`
126
+ }, (err, info) => {
127
+ if (err) console.error('send error', err);
128
+ else console.log('sent', info && info.messageId);
129
+ });
130
+ });
131
+ ```
132
+
133
+ Async/await style:
134
+
135
+ ```js
136
+ await auth.otp.generate(6); // generates and stores `auth.otp.code`
137
+ await auth.otp.set('user@example.com'); // saves OTP into memory/redis
138
+ await auth.otp.message({
139
+ to: 'user@example.com',
140
+ subject: 'Verify',
141
+ html: `Your code: <b>${auth.otp.code}</b>`
142
+ });
143
+ ```
144
+
145
+ ### Verify
146
+
147
+ ```js
148
+ // Promise style
149
+ try {
150
+ const ok = await auth.otp.verify({ check: 'user@example.com', code: '123456' });
151
+ console.log('verified', ok);
152
+ } catch (err) {
153
+ console.error('verify failed', err.message);
154
+ }
155
+
156
+ // Callback style also supported: auth.otp.verify({check, code}, callback)
157
+ ```
158
+
159
+ ### Resend and cooldown / max attempts
160
+
161
+ - `auth.otp.cooldown('30s')` or `auth.otp.cooldown(30000)` — set cooldown duration.
162
+ - `auth.otp.maxAttempt(5)` — set maximum attempts allowed.
163
+ - `auth.otp.resend(identifier)` — regenerate and resend OTP, observing cooldown and expiry rules.
164
+
165
+ `resend` returns the new code (promise style) or calls callback.
166
+
167
+ ---
168
+
169
+ ## Telegram integration
170
+
171
+ There are two ways to use Telegram flow:
172
+
173
+ 1. Use the built-in `senderConfig.via = 'telegram'` and call `auth.otp.setupTelegramBot(botToken)` — this starts a polling bot that asks users to share their phone via `/start`, and then matches the phone to in-memory/Redis OTP records and replies with the code.
174
+
175
+ 2. Developer-supplied custom sender (see below) — you can create your own bot and call it from `auth.use(...).send(...)` or register via `auth.register.sender()`.
176
+
177
+ **Important**: Only one bot using long polling must be running per bot token — if you get `409 Conflict` it's because another process or instance is already polling that bot token.
178
+
179
+ ---
180
+
181
+ ## Developer extensibility (custom senders)
182
+
183
+ You can register custom senders and use them:
184
+
185
+ ```js
186
+ // register a named sender function
187
+ auth.register.sender('consoleOtp', async ({ to, code }) => {
188
+ console.log(`[DEV SEND] send to ${to}: ${code}`);
189
+ });
190
+
191
+ // use it later (chainable)
192
+ await auth.use('consoleOtp').send({ to: '+998901234567', code: await auth.otp.generate(5) });
193
+ ```
194
+
195
+ ---
196
+
197
+ When a custom sender is registered, `auth.otp.message()` will first attempt the `customSender` before falling back to built-in providers.
198
+
199
+ ---
200
+
201
+ ## SessionManager
202
+
203
+ ```js
204
+ const SessionManager = require('./src/session'); // or auth.session after export
205
+ const sessions = new SessionManager({ storeTokens: 'redis' });
206
+
207
+ // create
208
+ const sessionId = await sessions.create('user123', { expiresIn: '2h' });
209
+
210
+ // verify
211
+ const userId = await sessions.verify(sessionId);
212
+
213
+ // destroy
214
+ await sessions.destroy(sessionId);
215
+ ```
216
+
217
+ Notes:
218
+ - `expiresIn` accepts numeric seconds or strings like `'30s'`, `'5m'`, `'1h'`, `'1d'`.
219
+
220
+ ---
221
+
222
+ ## Helpers
223
+
224
+ `helpers/helper.js` exposes utility functions used by managers:
225
+
226
+ - `generateSecureOTP(length, hashAlgorithm)` — returns secure numeric OTP string
227
+ - `parseTime(strOrNumber)` — converts `'1h' | '30s' | number` into milliseconds
228
+ - `resendGeneratedOTP(params)` — helper to send email via nodemailer (used by resend)
229
+ - `sendSMS(params)` — helper for sending SMS using supported providers or mock
230
+
231
+ ---
232
+
233
+ ## Error handling and notes
234
+
235
+ - Many methods support both **callback** and **Promise (async/await)** styles. When using Redis store, prefer **async/await** (callback variants intentionally return an error when Redis is selected).
236
+ - OTP storage keys are the user identifier you pass (email or phone number). Keep identifiers consistent.
237
+ - Be careful when using Telegram polling: do not run two instances with polling true for the same bot token (use webhooks or a single process).
238
+ - When configuring SMTP (non-Gmail), provide `host`, `port` and `secure` in `setSender()`.
239
+
240
+ ---
241
+
242
+ ## Suggested folder structure
243
+
244
+ ```
245
+ auth-verify/
246
+ ├─ README.md
247
+ ├─ package.json
248
+ ├─ src/
249
+ │ ├─ index.js // exports AuthVerify
250
+ │ ├─ jwt.js
251
+ │ ├─ otp.js
252
+ │ ├─ session.js
253
+ │ └─ helpers/helper.js
254
+ ```
255
+
256
+ ---
257
+
258
+ ## Contributing & License
259
+
260
+ Contributions welcome! Open issues / PRs for bugs, improvements, or API suggestions.
261
+
262
+ MIT © 2025 — Jahongir Sobirov