auth-verify 0.0.2 → 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 +213 -70
- 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
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const nodemailer = require("nodemailer");
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert time strings like "1h", "30m", "10s" to milliseconds
|
|
7
|
+
*/
|
|
8
|
+
function parseTime(str) {
|
|
9
|
+
if (typeof str === 'number') return str; // already ms
|
|
10
|
+
if (typeof str !== 'string') return 0;
|
|
11
|
+
|
|
12
|
+
const num = parseInt(str);
|
|
13
|
+
if (str.endsWith('h')) return num * 60 * 60 * 1000;
|
|
14
|
+
if (str.endsWith('m')) return num * 60 * 1000;
|
|
15
|
+
if (str.endsWith('s')) return num * 1000;
|
|
16
|
+
return num;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Generate a random numeric OTP of given length
|
|
21
|
+
*/
|
|
22
|
+
function generateSecureOTP(length = 6, hash = 'sha256') {
|
|
23
|
+
// Create random bytes
|
|
24
|
+
const randomBytes = crypto.randomBytes(32);
|
|
25
|
+
// Hash the bytes
|
|
26
|
+
const hashed = crypto.createHash(hash).update(randomBytes).digest('hex');
|
|
27
|
+
|
|
28
|
+
// Convert to numeric OTP
|
|
29
|
+
let otp = '';
|
|
30
|
+
for (let i = 0; i < hashed.length && otp.length < length; i++) {
|
|
31
|
+
const char = hashed[i];
|
|
32
|
+
if (!isNaN(char)) otp += char;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// If not enough digits, recursively generate more
|
|
36
|
+
if (otp.length < length) return generateSecureOTP(length, hash);
|
|
37
|
+
return otp.slice(0, length);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function otpSendingProcessByEmail({senderService, senderEmail, senderPass, senderHost, senderPort, senderSecure, receiverEmail, senderText, senderSubject, senderHtml}){
|
|
41
|
+
if(senderService == 'gmail'){
|
|
42
|
+
const transporter = nodemailer.createTransport({
|
|
43
|
+
service: senderService,
|
|
44
|
+
auth: {
|
|
45
|
+
user: senderEmail,
|
|
46
|
+
pass: senderPass, // not your normal password
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if(senderText && !senderHtml){
|
|
51
|
+
transporter.sendMail({
|
|
52
|
+
from: senderEmail,
|
|
53
|
+
to: receiverEmail,
|
|
54
|
+
subject: senderSubject,
|
|
55
|
+
text: senderText,
|
|
56
|
+
// html: senderHtml
|
|
57
|
+
}, (err, info) => {
|
|
58
|
+
if (err) return console.error("❌ Failed:", err);
|
|
59
|
+
// console.log("✅ Email sent:", info.response);
|
|
60
|
+
});
|
|
61
|
+
}else if (senderHtml && !senderText){
|
|
62
|
+
transporter.sendMail({
|
|
63
|
+
from: senderEmail,
|
|
64
|
+
to: receiverEmail,
|
|
65
|
+
subject: senderSubject,
|
|
66
|
+
// text: senderText,
|
|
67
|
+
html: senderHtml
|
|
68
|
+
}, (err, info) => {
|
|
69
|
+
if (err) return console.error("❌ Failed:", err);
|
|
70
|
+
// console.log("✅ Email sent:", info.response);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}else{
|
|
74
|
+
const transporter = nodemailer.createTransport({
|
|
75
|
+
host: senderHost,
|
|
76
|
+
port: senderPort,
|
|
77
|
+
secure: senderSecure, // true for 465, false for others
|
|
78
|
+
auth: {
|
|
79
|
+
user: senderEmail,
|
|
80
|
+
pass: senderPass,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if(senderText && !senderHtml){
|
|
85
|
+
transporter.sendMail({
|
|
86
|
+
from: senderEmail,
|
|
87
|
+
to: receiverEmail,
|
|
88
|
+
subject: senderSubject,
|
|
89
|
+
text: senderText,
|
|
90
|
+
// html: senderHtml
|
|
91
|
+
}, (err, info) => {
|
|
92
|
+
if (err) return console.error("❌ Failed:", err);
|
|
93
|
+
// console.log("✅ Email sent:", info.response);
|
|
94
|
+
});
|
|
95
|
+
}else if (senderHtml && !senderText){
|
|
96
|
+
transporter.sendMail({
|
|
97
|
+
from: senderEmail,
|
|
98
|
+
to: receiverEmail,
|
|
99
|
+
subject: senderSubject,
|
|
100
|
+
// text: senderText,
|
|
101
|
+
html: senderHtml
|
|
102
|
+
}, (err, info) => {
|
|
103
|
+
if (err) return console.error("❌ Failed:", err);
|
|
104
|
+
// console.log("✅ Email sent:", info.response);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function resendGeneratedOTP({from, to, pass, service, host, secure, port, subject, text, html, code}, callbackData){
|
|
111
|
+
if(callbackData && typeof callbackData == 'function'){
|
|
112
|
+
// const code = generateSecureOTP(data.code.length, this.hashAlgorithm);
|
|
113
|
+
let transporter;
|
|
114
|
+
if (service === 'gmail') {
|
|
115
|
+
transporter = nodemailer.createTransport({
|
|
116
|
+
service: 'gmail',
|
|
117
|
+
auth: { user: from, pass }
|
|
118
|
+
});
|
|
119
|
+
} else if (service === 'smtp') {
|
|
120
|
+
transporter = nodemailer.createTransport({
|
|
121
|
+
host,
|
|
122
|
+
port,
|
|
123
|
+
secure: !!secure,
|
|
124
|
+
auth: { user: from, pass }
|
|
125
|
+
});
|
|
126
|
+
} else {
|
|
127
|
+
throw new Error(`Unsupported email service: ${service}`);
|
|
128
|
+
}
|
|
129
|
+
// prepare mail
|
|
130
|
+
const mail = {
|
|
131
|
+
from,
|
|
132
|
+
to,
|
|
133
|
+
subject: subject || 'Your OTP Code',
|
|
134
|
+
text: text || `Your OTP is ${code}`,
|
|
135
|
+
html: html || `<p>Your OTP is <b>${code}</b></p>`
|
|
136
|
+
};
|
|
137
|
+
transporter.sendMail(mail, (err, info)=>{
|
|
138
|
+
if(err) return callbackData(new Error(err));
|
|
139
|
+
callbackData(null, info);
|
|
140
|
+
});
|
|
141
|
+
}else{
|
|
142
|
+
try{
|
|
143
|
+
let transporter;
|
|
144
|
+
if (service === 'gmail') {
|
|
145
|
+
transporter = nodemailer.createTransport({
|
|
146
|
+
service: 'gmail',
|
|
147
|
+
auth: { user: from, pass }
|
|
148
|
+
});
|
|
149
|
+
} else if (service === 'smtp') {
|
|
150
|
+
transporter = nodemailer.createTransport({
|
|
151
|
+
host,
|
|
152
|
+
port,
|
|
153
|
+
secure: !!secure,
|
|
154
|
+
auth: { user: from, pass: pass }
|
|
155
|
+
});
|
|
156
|
+
} else {
|
|
157
|
+
throw new Error(`Unsupported email service: ${service}`);
|
|
158
|
+
}
|
|
159
|
+
// prepare mail
|
|
160
|
+
const mail = {
|
|
161
|
+
from,
|
|
162
|
+
to,
|
|
163
|
+
subject: subject || 'Your OTP Code',
|
|
164
|
+
text: text || `Your OTP is ${code}`,
|
|
165
|
+
html: html || `<p>Your OTP is <b>${code}</b></p>`
|
|
166
|
+
};
|
|
167
|
+
await transporter.sendMail(mail);
|
|
168
|
+
}catch(err){
|
|
169
|
+
throw new Error(err);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function sendSMS({ provider, apiKey, apiSecret, from, to, text, mock = false }) {
|
|
175
|
+
try {
|
|
176
|
+
// 🧪 For testing only — no real SMS sent
|
|
177
|
+
if (mock) {
|
|
178
|
+
console.log(`📱 [Mock SMS via ${provider}]`);
|
|
179
|
+
console.log(`→ To: ${to}`);
|
|
180
|
+
console.log(`→ Message: ${text}`);
|
|
181
|
+
return { status: "SENT (mock)", to, text };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// 🔷 Infobip API
|
|
185
|
+
if (provider === 'infobip') {
|
|
186
|
+
await axios.post(
|
|
187
|
+
'https://api.infobip.com/sms/2/text/advanced',
|
|
188
|
+
{
|
|
189
|
+
messages: [{ from, destinations: [{ to }], text }],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
headers: {
|
|
193
|
+
Authorization: `App ${apiKey}`,
|
|
194
|
+
'Content-Type': 'application/json',
|
|
195
|
+
},
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
return { status: "SENT", provider: "Infobip", to };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 🟪 Vonage API
|
|
202
|
+
if (provider === 'vonage') {
|
|
203
|
+
await axios.post(
|
|
204
|
+
'https://rest.nexmo.com/sms/json',
|
|
205
|
+
{
|
|
206
|
+
api_key: apiKey,
|
|
207
|
+
api_secret: apiSecret,
|
|
208
|
+
to,
|
|
209
|
+
from,
|
|
210
|
+
text,
|
|
211
|
+
},
|
|
212
|
+
{ headers: { 'Content-Type': 'application/json' } }
|
|
213
|
+
);
|
|
214
|
+
return { status: "SENT", provider: "Vonage", to };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// 🟩 Twilio API
|
|
218
|
+
if (provider === 'twilio') {
|
|
219
|
+
await axios.post(
|
|
220
|
+
`https://api.twilio.com/2010-04-01/Accounts/${apiKey}/Messages.json`,
|
|
221
|
+
new URLSearchParams({
|
|
222
|
+
To: to,
|
|
223
|
+
From: from,
|
|
224
|
+
Body: text,
|
|
225
|
+
}),
|
|
226
|
+
{
|
|
227
|
+
auth: {
|
|
228
|
+
username: apiKey,
|
|
229
|
+
password: apiSecret,
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
return { status: "SENT", provider: "Twilio", to };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
throw new Error(`Unsupported SMS provider: ${provider}`);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
console.error("❌ SMS send failed:", err.message);
|
|
239
|
+
throw new Error(err.message);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// async function sendOTPwithTelegramBot(otpCode){
|
|
244
|
+
// bot.onText(/\/start/, (msg) => {
|
|
245
|
+
// bot.sendMessage(chatId, "Please share your phone number:", {
|
|
246
|
+
// reply_markup: {
|
|
247
|
+
// keyboard: [
|
|
248
|
+
// [
|
|
249
|
+
// {
|
|
250
|
+
// text: "📞 Share my phone number",
|
|
251
|
+
// request_contact: true, // 🔥 this requests the user's phone
|
|
252
|
+
// },
|
|
253
|
+
// ],
|
|
254
|
+
// ],
|
|
255
|
+
// resize_keyboard: true,
|
|
256
|
+
// one_time_keyboard: true,
|
|
257
|
+
// },
|
|
258
|
+
// });
|
|
259
|
+
// bot.sendMessage(msg.chat.id, `Your Verification code is <b>${otpCode}</b>`, {parse_mode: "HTML"});
|
|
260
|
+
// });
|
|
261
|
+
|
|
262
|
+
// bot.on("contact", (msg) => {
|
|
263
|
+
// const phoneNumber = msg.contact.phone_number;
|
|
264
|
+
// const firstName = msg.contact.first_name;
|
|
265
|
+
// const userId = msg.from.id;
|
|
266
|
+
|
|
267
|
+
// console.log("User shared phone:", phoneNumber);
|
|
268
|
+
|
|
269
|
+
// bot.sendMessage(
|
|
270
|
+
// msg.chat.id,
|
|
271
|
+
// `Thanks, ${firstName}! Your phone number is ${phoneNumber}.`
|
|
272
|
+
// );
|
|
273
|
+
|
|
274
|
+
// // Here you can verify or store it in your database
|
|
275
|
+
// });
|
|
276
|
+
// }
|
|
277
|
+
|
|
278
|
+
module.exports = {
|
|
279
|
+
parseTime,
|
|
280
|
+
generateSecureOTP,
|
|
281
|
+
otpSendingProcessByEmail,
|
|
282
|
+
resendGeneratedOTP,
|
|
283
|
+
sendSMS
|
|
284
|
+
};
|
package/src/jwt/index.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const jwt = require("jsonwebtoken");
|
|
2
|
+
const Redis = require("ioredis");
|
|
3
|
+
const { parseTime } = require('../helpers/helper');
|
|
4
|
+
|
|
5
|
+
class JWTManager {
|
|
6
|
+
constructor(secret, options = {}) {
|
|
7
|
+
if (!secret) throw new Error("JWT secret is required");
|
|
8
|
+
this.secret = secret;
|
|
9
|
+
this.storeType = options.storeTokens || "none";
|
|
10
|
+
|
|
11
|
+
if (this.storeType !== 'none') {
|
|
12
|
+
if (this.storeType === 'memory') {
|
|
13
|
+
this.tokenStore = new Map();
|
|
14
|
+
} else if (this.storeType === 'redis') {
|
|
15
|
+
this.redis = new Redis(options.redisUrl || "redis://localhost:6379");
|
|
16
|
+
} else {
|
|
17
|
+
throw new Error("{storeTokens} should be 'none', 'memory', or 'redis'");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Helper: convert expiry string like "1h" / "30m" / "10s" to milliseconds
|
|
23
|
+
_parseExpiry(expiry) {
|
|
24
|
+
if (typeof expiry === 'number') return expiry;
|
|
25
|
+
if (typeof expiry !== 'string') return 3600000; // default 1h
|
|
26
|
+
|
|
27
|
+
const num = parseInt(expiry);
|
|
28
|
+
if (expiry.endsWith('h')) return num * 60 * 60 * 1000;
|
|
29
|
+
if (expiry.endsWith('m')) return num * 60 * 1000;
|
|
30
|
+
if (expiry.endsWith('s')) return num * 1000;
|
|
31
|
+
return num;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async sign(payload, expiry, callback) {
|
|
35
|
+
if (typeof expiry === 'function') {
|
|
36
|
+
callback = expiry;
|
|
37
|
+
expiry = '1h'; // default
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const expiryMs = this._parseExpiry(expiry || '1h'); // fallback to '1h'
|
|
41
|
+
const expiryJwt = typeof expiry === 'number'
|
|
42
|
+
? Math.floor(expiry / 1000)
|
|
43
|
+
: (typeof expiry === 'string' ? expiry : '1h');
|
|
44
|
+
|
|
45
|
+
// Callback version
|
|
46
|
+
if(callback && typeof callback === 'function') {
|
|
47
|
+
if(this.storeType === 'redis') return callback(new Error("⚠️ Redis requires async/await use promise style functions"));
|
|
48
|
+
|
|
49
|
+
jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, token) => {
|
|
50
|
+
if(err) return callback(err);
|
|
51
|
+
|
|
52
|
+
if(this.storeType === 'memory') {
|
|
53
|
+
this.tokenStore.set(token, {payload, createdAt: Date.now()});
|
|
54
|
+
setTimeout(() => this.tokenStore.delete(token), expiryMs);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
callback(null, token);
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Async version
|
|
63
|
+
const token = await new Promise((res, rej) => {
|
|
64
|
+
jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, t) => {
|
|
65
|
+
if(err) rej(err);
|
|
66
|
+
else res(t);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if(this.storeType === 'memory') {
|
|
71
|
+
this.tokenStore.set(token, {payload, createdAt: Date.now()});
|
|
72
|
+
setTimeout(() => this.tokenStore.delete(token), expiryMs);
|
|
73
|
+
} else if(this.storeType === 'redis') {
|
|
74
|
+
await this.redis.set(token, JSON.stringify({payload, createdAt: Date.now()}), "EX", Math.floor(expiryMs/1000));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return token;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async verify(token, callback) {
|
|
81
|
+
// Callback version
|
|
82
|
+
if (callback && typeof callback === 'function') {
|
|
83
|
+
if (this.storeType === 'redis') {
|
|
84
|
+
return callback(new Error("⚠️ Redis requires async/await. Use Promise style."));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.storeType === 'memory' && !this.tokenStore.has(token)) {
|
|
88
|
+
return callback(new Error("❌ Token not found or revoked"));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
jwt.verify(token, this.secret, (err, decoded) => {
|
|
92
|
+
if (err) {
|
|
93
|
+
if (err.name === "TokenExpiredError") return callback(new Error("❌ Token expired!"));
|
|
94
|
+
if (err.name === "JsonWebTokenError") return callback(new Error("❌ Invalid token!"));
|
|
95
|
+
return callback(new Error("❌ JWT error: " + err.message));
|
|
96
|
+
}
|
|
97
|
+
callback(null, decoded);
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Async / Promise version
|
|
103
|
+
try {
|
|
104
|
+
if (this.storeType === 'memory' && !this.tokenStore.has(token)) {
|
|
105
|
+
throw new Error("❌ Token not found or revoked");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (this.storeType === 'redis') {
|
|
109
|
+
const data = await this.redis.get(token);
|
|
110
|
+
if (!data) throw new Error("❌ Token not found or revoked");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const decoded = jwt.verify(token, this.secret);
|
|
114
|
+
return decoded;
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error("❌ JWT verification failed: " + err.message);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async decode(token, callback) {
|
|
121
|
+
try {
|
|
122
|
+
const decoded = jwt.decode(token);
|
|
123
|
+
// if (callback && typeof callback === 'function') return callback(null, decoded);
|
|
124
|
+
return decoded;
|
|
125
|
+
} catch (err) {
|
|
126
|
+
// if (callback && typeof callback === 'function') return callback(err);
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Optional: manual token revoke for memory/redis
|
|
132
|
+
async revoke(token, revokeTime = 0) {
|
|
133
|
+
// function parseTime(str) {
|
|
134
|
+
// if (typeof str === 'number') return str; // already in ms
|
|
135
|
+
// if (typeof str !== 'string') return 0;
|
|
136
|
+
|
|
137
|
+
// const num = parseInt(str);
|
|
138
|
+
// if (str.endsWith('h')) return num * 60 * 60 * 1000;
|
|
139
|
+
// if (str.endsWith('m')) return num * 60 * 1000;
|
|
140
|
+
// if (str.endsWith('s')) return num * 1000;
|
|
141
|
+
// return num;
|
|
142
|
+
// }
|
|
143
|
+
|
|
144
|
+
const revokeMs = parseTime(revokeTime);
|
|
145
|
+
const revokeAfter = Date.now() + revokeMs;
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
if (this.storeType === 'memory') {
|
|
149
|
+
if(revokeTime == 0){
|
|
150
|
+
this.tokenStore.delete(token);
|
|
151
|
+
}else{
|
|
152
|
+
setTimeout(() => this.tokenStore.delete(token), revokeAfter);
|
|
153
|
+
}
|
|
154
|
+
}else if(this.storeType == 'redis'){
|
|
155
|
+
if(revokeTime == 0){
|
|
156
|
+
await this.redis.del(token);
|
|
157
|
+
}else{
|
|
158
|
+
await this.redis.pexpire(token, revokeAfter);
|
|
159
|
+
}
|
|
160
|
+
}else{
|
|
161
|
+
throw new Error("❌ {storeTokens} should be 'memory' or 'redis' or 'none'");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async isRevoked(token) {
|
|
166
|
+
if(this.storeType === 'memory') {
|
|
167
|
+
const data = this.tokenStore.get(token);
|
|
168
|
+
return data?.revokedUntil && data.revokedUntil > Date.now();
|
|
169
|
+
} else if(this.storeType === 'redis') {
|
|
170
|
+
const dataRaw = await this.redis.get(token);
|
|
171
|
+
if (!dataRaw) return true;
|
|
172
|
+
const data = JSON.parse(dataRaw);
|
|
173
|
+
return data?.revokedUntil && data.revokedUntil > Date.now();
|
|
174
|
+
} else {
|
|
175
|
+
return false; // no store, can't revoke
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async revokeUntil(token, timestamp){
|
|
180
|
+
// function parseTime(str) {
|
|
181
|
+
// if (typeof str === 'number') return str; // already in ms
|
|
182
|
+
// if (typeof str !== 'string') return 0;
|
|
183
|
+
|
|
184
|
+
// const num = parseInt(str);
|
|
185
|
+
// if (str.endsWith('h')) return num * 60 * 60 * 1000;
|
|
186
|
+
// if (str.endsWith('m')) return num * 60 * 1000;
|
|
187
|
+
// if (str.endsWith('s')) return num * 1000;
|
|
188
|
+
// return num;
|
|
189
|
+
// }
|
|
190
|
+
|
|
191
|
+
const revokeMs = parseTime(timestamp);
|
|
192
|
+
const revokedUntil = Date.now() + revokeMs;
|
|
193
|
+
|
|
194
|
+
if(this.storeType == 'memory'){
|
|
195
|
+
const data = this.tokenStore.get(token) || {};
|
|
196
|
+
this.tokenStore.set(token, {...data, revokedUntil: revokedUntil});
|
|
197
|
+
}else if(this.storeType == 'redis'){
|
|
198
|
+
const dataRaw = await this.redis.get(token);
|
|
199
|
+
const data = dataRaw ? JSON.parse(dataRaw) : {};
|
|
200
|
+
data.revokedUntil = revokedUntil;
|
|
201
|
+
await this.redis.set(token, JSON.stringify(data));
|
|
202
|
+
}else{
|
|
203
|
+
throw new Error("{storeTokens} should be 'memory' or 'redis'");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = JWTManager;
|