auth-verify 0.0.2 → 1.1.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 +79 -175
- package/package.json +10 -4
- package/readme.md +247 -70
- package/src/helpers/helper.js +284 -0
- package/src/jwt/cookie/index.js +29 -0
- package/src/jwt/index.js +392 -0
- package/src/otp/index.js +1052 -0
- package/src/session/index.js +81 -0
- package/test.js +178 -31
- package/auth-verify.js +0 -182
- package/authverify.db +0 -0
- package/otp.db +0 -0
package/src/jwt/index.js
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// const jwt = require("jsonwebtoken");
|
|
2
|
+
// const Redis = require("ioredis");
|
|
3
|
+
// const { parseTime } = require('../helpers/helper');
|
|
4
|
+
// const CookieManager = require('./cookie');
|
|
5
|
+
|
|
6
|
+
// class JWTManager {
|
|
7
|
+
// constructor(secret, options = {}) {
|
|
8
|
+
// if (!secret) throw new Error("JWT secret is required");
|
|
9
|
+
// this.secret = secret;
|
|
10
|
+
// this.storeType = options.storeTokens || "none";
|
|
11
|
+
|
|
12
|
+
// if (this.storeType !== 'none') {
|
|
13
|
+
// if (this.storeType === 'memory') {
|
|
14
|
+
// this.tokenStore = new Map();
|
|
15
|
+
// } else if (this.storeType === 'redis') {
|
|
16
|
+
// this.redis = new Redis(options.redisUrl || "redis://localhost:6379");
|
|
17
|
+
// } else {
|
|
18
|
+
// throw new Error("{storeTokens} should be 'none', 'memory', or 'redis'");
|
|
19
|
+
// }
|
|
20
|
+
// }
|
|
21
|
+
// }
|
|
22
|
+
|
|
23
|
+
// // Helper: convert expiry string like "1h" / "30m" / "10s" to milliseconds
|
|
24
|
+
// _parseExpiry(expiry) {
|
|
25
|
+
// if (typeof expiry === 'number') return expiry;
|
|
26
|
+
// if (typeof expiry !== 'string') return 3600000; // default 1h
|
|
27
|
+
|
|
28
|
+
// const num = parseInt(expiry);
|
|
29
|
+
// if (expiry.endsWith('h')) return num * 60 * 60 * 1000;
|
|
30
|
+
// if (expiry.endsWith('m')) return num * 60 * 1000;
|
|
31
|
+
// if (expiry.endsWith('s')) return num * 1000;
|
|
32
|
+
// return num;
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
// async sign(payload, expiry, options = {}, callback) {
|
|
36
|
+
// if (typeof expiry === 'function') {
|
|
37
|
+
// callback = expiry;
|
|
38
|
+
// expiry = '1h'; // default
|
|
39
|
+
// }
|
|
40
|
+
|
|
41
|
+
// const expiryMs = this._parseExpiry(expiry || '1h'); // fallback to '1h'
|
|
42
|
+
// const expiryJwt = typeof expiry === 'number'
|
|
43
|
+
// ? Math.floor(expiry / 1000)
|
|
44
|
+
// : (typeof expiry === 'string' ? expiry : '1h');
|
|
45
|
+
|
|
46
|
+
// // Auto-set cookie if res provided
|
|
47
|
+
// if(typeof options == 'function'){
|
|
48
|
+
// callback = options;
|
|
49
|
+
// }
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
// // Callback version
|
|
53
|
+
// if(callback && typeof callback === 'function') {
|
|
54
|
+
// if(this.storeType === 'redis') return callback(new Error("⚠️ Redis requires async/await use promise style functions"));
|
|
55
|
+
|
|
56
|
+
// jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, token) => {
|
|
57
|
+
// if(err) return callback(err);
|
|
58
|
+
|
|
59
|
+
// if (options.res) {
|
|
60
|
+
// CookieManager.setCookie(options.res, 'jwt_token', token, {
|
|
61
|
+
// httpOnly: true,
|
|
62
|
+
// secure: options.secure ?? true,
|
|
63
|
+
// sameSite: 'Strict',
|
|
64
|
+
// maxAge: this._parseTime(expiresIn),
|
|
65
|
+
// });
|
|
66
|
+
// }else if(this.storeType === 'memory') {
|
|
67
|
+
// this.tokenStore.set(token, {payload, createdAt: Date.now()});
|
|
68
|
+
// setTimeout(() => this.tokenStore.delete(token), expiryMs);
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
// callback(null, token);
|
|
72
|
+
// });
|
|
73
|
+
// return;
|
|
74
|
+
// }
|
|
75
|
+
|
|
76
|
+
// // Async version
|
|
77
|
+
// const token = await new Promise((res, rej) => {
|
|
78
|
+
// jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, t) => {
|
|
79
|
+
// if(err) rej(err);
|
|
80
|
+
// else res(t);
|
|
81
|
+
// });
|
|
82
|
+
// });
|
|
83
|
+
|
|
84
|
+
// if (options.res) {
|
|
85
|
+
// CookieManager.setCookie(options.res, 'jwt_token', token, {
|
|
86
|
+
// httpOnly: true,
|
|
87
|
+
// secure: options.secure ?? true,
|
|
88
|
+
// sameSite: 'Strict',
|
|
89
|
+
// maxAge: this._parseExpiry(expiresIn),
|
|
90
|
+
// });
|
|
91
|
+
// }else if(this.storeType === 'memory') {
|
|
92
|
+
// this.tokenStore.set(token, {payload, createdAt: Date.now()});
|
|
93
|
+
// setTimeout(() => this.tokenStore.delete(token), expiryMs);
|
|
94
|
+
// } else if(this.storeType === 'redis') {
|
|
95
|
+
// await this.redis.set(token, JSON.stringify({payload, createdAt: Date.now()}), "EX", Math.floor(expiryMs/1000));
|
|
96
|
+
// }
|
|
97
|
+
|
|
98
|
+
// return token;
|
|
99
|
+
// }
|
|
100
|
+
|
|
101
|
+
// async verify(token, callback) {
|
|
102
|
+
|
|
103
|
+
// let cookieToken;
|
|
104
|
+
// if(typeof token == 'object' && token.headers){
|
|
105
|
+
// cookieToken = CookieManager.getCookie(token, 'jwt_token');
|
|
106
|
+
// if (!token) throw new Error('JWT not found in cookies');
|
|
107
|
+
|
|
108
|
+
// try{
|
|
109
|
+
// const decoded = jwt.verify(cookieToken, this.secret);
|
|
110
|
+
// return decoded;
|
|
111
|
+
// }catch(err){
|
|
112
|
+
// throw new Error('Invalid or expired token');
|
|
113
|
+
// }
|
|
114
|
+
// }
|
|
115
|
+
|
|
116
|
+
// // Callback version
|
|
117
|
+
// if (callback && typeof callback === 'function') {
|
|
118
|
+
// if (this.storeType === 'redis') {
|
|
119
|
+
// return callback(new Error("⚠️ Redis requires async/await. Use Promise style."));
|
|
120
|
+
// }
|
|
121
|
+
|
|
122
|
+
// if (this.storeType === 'memory' && !this.tokenStore.has(token)) {
|
|
123
|
+
// return callback(new Error("❌ Token not found or revoked"));
|
|
124
|
+
// }
|
|
125
|
+
|
|
126
|
+
// jwt.verify(token, this.secret, (err, decoded) => {
|
|
127
|
+
// if (err) {
|
|
128
|
+
// if (err.name === "TokenExpiredError") return callback(new Error("❌ Token expired!"));
|
|
129
|
+
// if (err.name === "JsonWebTokenError") return callback(new Error("❌ Invalid token!"));
|
|
130
|
+
// return callback(new Error("❌ JWT error: " + err.message));
|
|
131
|
+
// }
|
|
132
|
+
// callback(null, decoded);
|
|
133
|
+
// });
|
|
134
|
+
// return;
|
|
135
|
+
// }
|
|
136
|
+
|
|
137
|
+
// // Async / Promise version
|
|
138
|
+
// try {
|
|
139
|
+
// if (this.storeType === 'memory' && !this.tokenStore.has(token)) {
|
|
140
|
+
// throw new Error("❌ Token not found or revoked");
|
|
141
|
+
// }
|
|
142
|
+
|
|
143
|
+
// if (this.storeType === 'redis') {
|
|
144
|
+
// const data = await this.redis.get(token);
|
|
145
|
+
// if (!data) throw new Error("❌ Token not found or revoked");
|
|
146
|
+
// }
|
|
147
|
+
|
|
148
|
+
// const decoded = jwt.verify(token, this.secret);
|
|
149
|
+
// return decoded;
|
|
150
|
+
// } catch (err) {
|
|
151
|
+
// throw new Error("❌ JWT verification failed: " + err.message);
|
|
152
|
+
// }
|
|
153
|
+
// }
|
|
154
|
+
|
|
155
|
+
// async decode(token, callback) {
|
|
156
|
+
// try {
|
|
157
|
+
// const decoded = jwt.decode(token);
|
|
158
|
+
// // if (callback && typeof callback === 'function') return callback(null, decoded);
|
|
159
|
+
// return decoded;
|
|
160
|
+
// } catch (err) {
|
|
161
|
+
// // if (callback && typeof callback === 'function') return callback(err);
|
|
162
|
+
// throw err;
|
|
163
|
+
// }
|
|
164
|
+
// }
|
|
165
|
+
|
|
166
|
+
// // Optional: manual token revoke for memory/redis
|
|
167
|
+
// async revoke(token, revokeTime = 0) {
|
|
168
|
+
// // function parseTime(str) {
|
|
169
|
+
// // if (typeof str === 'number') return str; // already in ms
|
|
170
|
+
// // if (typeof str !== 'string') return 0;
|
|
171
|
+
|
|
172
|
+
// // const num = parseInt(str);
|
|
173
|
+
// // if (str.endsWith('h')) return num * 60 * 60 * 1000;
|
|
174
|
+
// // if (str.endsWith('m')) return num * 60 * 1000;
|
|
175
|
+
// // if (str.endsWith('s')) return num * 1000;
|
|
176
|
+
// // return num;
|
|
177
|
+
// // }
|
|
178
|
+
|
|
179
|
+
// const revokeMs = parseTime(revokeTime);
|
|
180
|
+
// const revokeAfter = Date.now() + revokeMs;
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
// if (this.storeType === 'memory') {
|
|
184
|
+
// if(revokeTime == 0){
|
|
185
|
+
// this.tokenStore.delete(token);
|
|
186
|
+
// }else{
|
|
187
|
+
// setTimeout(() => this.tokenStore.delete(token), revokeAfter);
|
|
188
|
+
// }
|
|
189
|
+
// }else if(this.storeType == 'redis'){
|
|
190
|
+
// if(revokeTime == 0){
|
|
191
|
+
// await this.redis.del(token);
|
|
192
|
+
// }else{
|
|
193
|
+
// await this.redis.pexpire(token, revokeAfter);
|
|
194
|
+
// }
|
|
195
|
+
// }else{
|
|
196
|
+
// throw new Error("❌ {storeTokens} should be 'memory' or 'redis' or 'none'");
|
|
197
|
+
// }
|
|
198
|
+
// }
|
|
199
|
+
|
|
200
|
+
// async isRevoked(token) {
|
|
201
|
+
// if(this.storeType === 'memory') {
|
|
202
|
+
// const data = this.tokenStore.get(token);
|
|
203
|
+
// return data?.revokedUntil && data.revokedUntil > Date.now();
|
|
204
|
+
// } else if(this.storeType === 'redis') {
|
|
205
|
+
// const dataRaw = await this.redis.get(token);
|
|
206
|
+
// if (!dataRaw) return true;
|
|
207
|
+
// const data = JSON.parse(dataRaw);
|
|
208
|
+
// return data?.revokedUntil && data.revokedUntil > Date.now();
|
|
209
|
+
// } else {
|
|
210
|
+
// return false; // no store, can't revoke
|
|
211
|
+
// }
|
|
212
|
+
// }
|
|
213
|
+
|
|
214
|
+
// async revokeUntil(token, timestamp){
|
|
215
|
+
// // function parseTime(str) {
|
|
216
|
+
// // if (typeof str === 'number') return str; // already in ms
|
|
217
|
+
// // if (typeof str !== 'string') return 0;
|
|
218
|
+
|
|
219
|
+
// // const num = parseInt(str);
|
|
220
|
+
// // if (str.endsWith('h')) return num * 60 * 60 * 1000;
|
|
221
|
+
// // if (str.endsWith('m')) return num * 60 * 1000;
|
|
222
|
+
// // if (str.endsWith('s')) return num * 1000;
|
|
223
|
+
// // return num;
|
|
224
|
+
// // }
|
|
225
|
+
|
|
226
|
+
// const revokeMs = parseTime(timestamp);
|
|
227
|
+
// const revokedUntil = Date.now() + revokeMs;
|
|
228
|
+
|
|
229
|
+
// if(this.storeType == 'memory'){
|
|
230
|
+
// const data = this.tokenStore.get(token) || {};
|
|
231
|
+
// this.tokenStore.set(token, {...data, revokedUntil: revokedUntil});
|
|
232
|
+
// }else if(this.storeType == 'redis'){
|
|
233
|
+
// const dataRaw = await this.redis.get(token);
|
|
234
|
+
// const data = dataRaw ? JSON.parse(dataRaw) : {};
|
|
235
|
+
// data.revokedUntil = revokedUntil;
|
|
236
|
+
// await this.redis.set(token, JSON.stringify(data));
|
|
237
|
+
// }else{
|
|
238
|
+
// throw new Error("{storeTokens} should be 'memory' or 'redis'");
|
|
239
|
+
// }
|
|
240
|
+
// }
|
|
241
|
+
// }
|
|
242
|
+
|
|
243
|
+
// module.exports = JWTManager;
|
|
244
|
+
|
|
245
|
+
const jwt = require("jsonwebtoken");
|
|
246
|
+
const Redis = require("ioredis");
|
|
247
|
+
const CookieManager = require("./cookie");
|
|
248
|
+
|
|
249
|
+
class JWTManager {
|
|
250
|
+
constructor(secret, options = {}) {
|
|
251
|
+
if (!secret) throw new Error("JWT secret is required");
|
|
252
|
+
this.secret = secret;
|
|
253
|
+
this.storeType = options.storeTokens || "none";
|
|
254
|
+
|
|
255
|
+
if (this.storeType === "memory") {
|
|
256
|
+
this.tokenStore = new Map();
|
|
257
|
+
} else if (this.storeType === "redis") {
|
|
258
|
+
this.redis = new Redis(options.redisUrl || "redis://localhost:6379");
|
|
259
|
+
} else if (this.storeType !== "none") {
|
|
260
|
+
throw new Error("{storeTokens} must be 'none', 'memory', or 'redis'");
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
_parseExpiry(expiry) {
|
|
265
|
+
if (typeof expiry === "number") return expiry;
|
|
266
|
+
if (typeof expiry !== "string") return 3600000; // default 1h
|
|
267
|
+
const num = parseInt(expiry);
|
|
268
|
+
if (expiry.endsWith("h")) return num * 3600000;
|
|
269
|
+
if (expiry.endsWith("m")) return num * 60000;
|
|
270
|
+
if (expiry.endsWith("s")) return num * 1000;
|
|
271
|
+
return num;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async sign(payload, expiry = "1h", options = {}, callback) {
|
|
275
|
+
if (typeof expiry === "function") {
|
|
276
|
+
callback = expiry;
|
|
277
|
+
expiry = "1h";
|
|
278
|
+
}
|
|
279
|
+
if (typeof options === "function") {
|
|
280
|
+
callback = options;
|
|
281
|
+
options = {};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const expiryMs = this._parseExpiry(expiry);
|
|
285
|
+
const expiryJwt = typeof expiry === "number"
|
|
286
|
+
? Math.floor(expiry / 1000)
|
|
287
|
+
: expiry;
|
|
288
|
+
|
|
289
|
+
const createToken = () =>
|
|
290
|
+
new Promise((resolve, reject) => {
|
|
291
|
+
jwt.sign(payload, this.secret, { expiresIn: expiryJwt }, (err, token) => {
|
|
292
|
+
if (err) return reject(err);
|
|
293
|
+
resolve(token);
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
const token = await createToken();
|
|
298
|
+
|
|
299
|
+
// Save token if needed
|
|
300
|
+
if (this.storeType === "memory") {
|
|
301
|
+
this.tokenStore.set(token, { payload, createdAt: Date.now() });
|
|
302
|
+
setTimeout(() => this.tokenStore.delete(token), expiryMs);
|
|
303
|
+
} else if (this.storeType === "redis") {
|
|
304
|
+
await this.redis.set(token, JSON.stringify({ payload, createdAt: Date.now() }), "EX", Math.floor(expiryMs / 1000));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Auto cookie support
|
|
308
|
+
if (options.res) {
|
|
309
|
+
CookieManager.setCookie(options.res, "jwt_token", token, {
|
|
310
|
+
httpOnly: true,
|
|
311
|
+
secure: options.secure ?? true,
|
|
312
|
+
sameSite: "Strict",
|
|
313
|
+
maxAge: expiryMs,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (callback) return callback(null, token);
|
|
318
|
+
return token;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
async verify(input, callback) {
|
|
322
|
+
let token = input;
|
|
323
|
+
|
|
324
|
+
// If request object provided
|
|
325
|
+
if (typeof input === "object" && input.headers) {
|
|
326
|
+
token =
|
|
327
|
+
CookieManager.getCookie(input, "jwt_token") ||
|
|
328
|
+
(input.headers.authorization
|
|
329
|
+
? input.headers.authorization.replace("Bearer ", "")
|
|
330
|
+
: null);
|
|
331
|
+
|
|
332
|
+
if (!token) throw new Error("JWT not found in cookies or headers");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try {
|
|
336
|
+
const decoded = jwt.verify(token, this.secret);
|
|
337
|
+
|
|
338
|
+
if (this.storeType === "memory" && !this.tokenStore.has(token))
|
|
339
|
+
throw new Error("Token not found or revoked");
|
|
340
|
+
|
|
341
|
+
if (this.storeType === "redis") {
|
|
342
|
+
const data = await this.redis.get(token);
|
|
343
|
+
if (!data) throw new Error("Token not found or revoked");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (callback) return callback(null, decoded);
|
|
347
|
+
return decoded;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
if (callback) return callback(err);
|
|
350
|
+
throw new Error("JWT verification failed: " + err.message);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async decode(token) {
|
|
355
|
+
return jwt.decode(token);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async revoke(token, revokeTime = 0) {
|
|
359
|
+
const revokeMs = this._parseExpiry(revokeTime);
|
|
360
|
+
|
|
361
|
+
if (this.storeType === "memory") {
|
|
362
|
+
if (revokeTime === 0) this.tokenStore.delete(token);
|
|
363
|
+
else setTimeout(() => this.tokenStore.delete(token), revokeMs);
|
|
364
|
+
} else if (this.storeType === "redis") {
|
|
365
|
+
if (revokeTime === 0) await this.redis.del(token);
|
|
366
|
+
else await this.redis.pexpire(token, revokeMs);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
async isRevoked(token) {
|
|
371
|
+
if (this.storeType === "memory") return !this.tokenStore.has(token);
|
|
372
|
+
if (this.storeType === "redis") return !(await this.redis.get(token));
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
async revokeUntil(token, timestamp) {
|
|
377
|
+
const revokeMs = this._parseExpiry(timestamp);
|
|
378
|
+
const revokedUntil = Date.now() + revokeMs;
|
|
379
|
+
|
|
380
|
+
if (this.storeType === "memory") {
|
|
381
|
+
const data = this.tokenStore.get(token) || {};
|
|
382
|
+
this.tokenStore.set(token, { ...data, revokedUntil });
|
|
383
|
+
} else if (this.storeType === "redis") {
|
|
384
|
+
const dataRaw = await this.redis.get(token);
|
|
385
|
+
const data = dataRaw ? JSON.parse(dataRaw) : {};
|
|
386
|
+
data.revokedUntil = revokedUntil;
|
|
387
|
+
await this.redis.set(token, JSON.stringify(data));
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
module.exports = JWTManager;
|