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 +79 -175
- package/package.json +12 -6
- package/readme.md +262 -0
- package/src/helpers/helper.js +284 -0
- package/src/jwt/index.js +208 -0
- package/src/otp/index.js +1052 -0
- package/src/session/index.js +81 -0
- package/test.js +161 -34
- package/auth-verify.js +0 -182
- package/authverify.db +0 -0
- package/otp.db +0 -0
package/index.js
CHANGED
|
@@ -1,182 +1,86 @@
|
|
|
1
|
-
const
|
|
2
|
-
const
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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 =
|
|
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
|
-
"
|
|
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
|
-
"
|
|
9
|
+
"redis": "^5.8.3",
|
|
10
|
+
"twilio": "^5.10.3",
|
|
11
|
+
"uuid": "^13.0.0"
|
|
7
12
|
},
|
|
8
13
|
"name": "auth-verify",
|
|
9
|
-
"version": "
|
|
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
|