auth-verify 0.0.1
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/auth-verify.js +182 -0
- package/authverify.db +0 -0
- package/index.js +182 -0
- package/otp.db +0 -0
- package/package.json +42 -0
- package/test.js +34 -0
package/auth-verify.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
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
|
|
18
|
+
}
|
|
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);
|
|
133
|
+
|
|
134
|
+
// console.log(`Code was got`);
|
|
135
|
+
|
|
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
|
+
}
|
|
147
|
+
}
|
|
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
|
+
});
|
|
166
|
+
}
|
|
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
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = Verifier;
|
package/authverify.db
ADDED
|
Binary file
|
package/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
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
|
|
18
|
+
}
|
|
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);
|
|
133
|
+
|
|
134
|
+
// console.log(`Code was got`);
|
|
135
|
+
|
|
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
|
+
}
|
|
147
|
+
}
|
|
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
|
+
});
|
|
166
|
+
}
|
|
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
|
+
}
|
|
181
|
+
|
|
182
|
+
module.exports = Verifier;
|
package/otp.db
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"dependencies": {
|
|
3
|
+
"crypto": "^1.0.1",
|
|
4
|
+
"express": "^5.1.0",
|
|
5
|
+
"nodemailer": "^7.0.6",
|
|
6
|
+
"sqlite3": "^5.1.7"
|
|
7
|
+
},
|
|
8
|
+
"name": "auth-verify",
|
|
9
|
+
"version": "0.0.1",
|
|
10
|
+
"description": "A simple Node.js library for sending and verifying OTP via email",
|
|
11
|
+
"main": "index.js",
|
|
12
|
+
"devDependencies": {},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node test.js"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/jahongir2007/auth-verify.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"auth",
|
|
22
|
+
"authentication",
|
|
23
|
+
"otp",
|
|
24
|
+
"email-verification",
|
|
25
|
+
"one-time-password",
|
|
26
|
+
"verification",
|
|
27
|
+
"nodejs",
|
|
28
|
+
"nodemailer",
|
|
29
|
+
"security",
|
|
30
|
+
"signup",
|
|
31
|
+
"login",
|
|
32
|
+
"two-factor",
|
|
33
|
+
"2fa",
|
|
34
|
+
"email"
|
|
35
|
+
],
|
|
36
|
+
"author": "Jahongir Sobirov",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/jahongir2007/auth-verify/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://jahongir2007.github.io/auth-verify/"
|
|
42
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const Verifier = require("./Verifier"); // or "./index.js" if you renamed it
|
|
2
|
+
|
|
3
|
+
const verifier = new Verifier({
|
|
4
|
+
serv: "Gmail",
|
|
5
|
+
sender: "your_email@gmail.com",
|
|
6
|
+
pass: "your_app_password",
|
|
7
|
+
otp: {
|
|
8
|
+
length: 6,
|
|
9
|
+
expMin: 5,
|
|
10
|
+
limit: 5,
|
|
11
|
+
cooldown: 60
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Test sending OTP
|
|
16
|
+
verifier
|
|
17
|
+
.subject("Your OTP: {otp}")
|
|
18
|
+
.text("Your OTP code is {otp}")
|
|
19
|
+
.html("<h1>{otp}</h1>")
|
|
20
|
+
.sendTo("test_user@example.com", (err, success) => {
|
|
21
|
+
if (err) return console.error("Send failed:", err.message);
|
|
22
|
+
console.log("✅ OTP sent successfully!");
|
|
23
|
+
|
|
24
|
+
// Test verifying the same OTP
|
|
25
|
+
verifier.getOTP("test_user@example.com", (err, data) => {
|
|
26
|
+
if (err) return console.error(err);
|
|
27
|
+
console.log("Current OTP in DB:", data);
|
|
28
|
+
|
|
29
|
+
verifier.code(data.code).verifyFor("test_user@example.com", (err, verified) => {
|
|
30
|
+
if (err) return console.error(err);
|
|
31
|
+
console.log("Verified?", verified);
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
});
|