ecwt 0.2.1-beta.6 → 0.2.5
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/README.md +197 -157
- package/bun.lock +948 -0
- package/dist/main.cjs +249 -435
- package/dist/main.d.ts +160 -0
- package/dist/main.js +246 -0
- package/package.json +28 -23
- package/bun.lockb +0 -0
- package/dist/types/factory.d.ts +0 -97
- package/dist/types/main.d.ts +0 -4
- package/dist/types/token.d.ts +0 -66
- package/dist/types/utils/base62.d.ts +0 -2
- package/dist/types/utils/errors.d.ts +0 -38
- package/dist/types/utils/time.d.ts +0 -6
- package/src/factory.js +0 -385
- package/src/main.js +0 -13
- package/src/token.js +0 -106
- package/src/utils/base62.js +0 -4
- package/src/utils/errors.js +0 -58
- package/src/utils/time.js +0 -17
package/dist/main.cjs
CHANGED
|
@@ -1,461 +1,275 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
//#region rolldown:runtime
|
|
2
3
|
var __create = Object.create;
|
|
3
4
|
var __defProp = Object.defineProperty;
|
|
4
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
9
|
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
19
18
|
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/main.js
|
|
31
|
-
var main_exports = {};
|
|
32
|
-
__export(main_exports, {
|
|
33
|
-
Ecwt: () => Ecwt,
|
|
34
|
-
EcwtExpiredError: () => EcwtExpiredError,
|
|
35
|
-
EcwtFactory: () => EcwtFactory,
|
|
36
|
-
EcwtInvalidError: () => EcwtInvalidError,
|
|
37
|
-
EcwtParseError: () => EcwtParseError,
|
|
38
|
-
EcwtRevokedError: () => EcwtRevokedError
|
|
39
|
-
});
|
|
40
|
-
module.exports = __toCommonJS(main_exports);
|
|
41
|
-
|
|
42
|
-
// src/factory.js
|
|
43
|
-
var import_snowflake = require("@kirick/snowflake");
|
|
44
|
-
var import_cbor_x = require("cbor-x");
|
|
45
|
-
var import_evilcrypt = require("evilcrypt");
|
|
46
|
-
var import_lru_cache = require("lru-cache");
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
24
|
+
//#endregion
|
|
25
|
+
const cbor_x = __toESM(require("cbor-x"));
|
|
26
|
+
const evilcrypt = __toESM(require("evilcrypt"));
|
|
27
|
+
const base_x = __toESM(require("base-x"));
|
|
52
28
|
|
|
53
|
-
|
|
29
|
+
//#region src/token.ts
|
|
54
30
|
var Ecwt = class {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.#ttl_initial = ttl_initial;
|
|
105
|
-
this.token = token;
|
|
106
|
-
this.id = snowflake.toBase62();
|
|
107
|
-
this.snowflake = snowflake;
|
|
108
|
-
this.ts_expired = this.#getTimestampExpired();
|
|
109
|
-
this.data = Object.freeze(data);
|
|
110
|
-
}
|
|
111
|
-
#getTimestampExpired() {
|
|
112
|
-
if (this.#ttl_initial === null) {
|
|
113
|
-
return null;
|
|
114
|
-
}
|
|
115
|
-
return toSeconds(this.snowflake.timestamp) + this.#ttl_initial;
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Actual time to live in seconds.
|
|
119
|
-
* @returns {number | null} -
|
|
120
|
-
*/
|
|
121
|
-
getTTL() {
|
|
122
|
-
if (this.#ttl_initial === null) {
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
return this.#ttl_initial - toSeconds(Date.now() - this.snowflake.timestamp);
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* Revokes token.
|
|
129
|
-
* @returns {Promise<void>} -
|
|
130
|
-
*/
|
|
131
|
-
/* async */
|
|
132
|
-
revoke() {
|
|
133
|
-
return this.#ecwtFactory._revoke({
|
|
134
|
-
token_id: this.id,
|
|
135
|
-
ts_ms_created: this.snowflake.timestamp,
|
|
136
|
-
ttl_initial: this.#ttl_initial
|
|
137
|
-
});
|
|
138
|
-
}
|
|
31
|
+
/** Token string representation. */
|
|
32
|
+
token;
|
|
33
|
+
/** Token ID. */
|
|
34
|
+
id;
|
|
35
|
+
/** Snowflake associated with token. */
|
|
36
|
+
snowflake;
|
|
37
|
+
/** Data stored in token. */
|
|
38
|
+
data;
|
|
39
|
+
ecwtFactory;
|
|
40
|
+
ttl_initial;
|
|
41
|
+
/**
|
|
42
|
+
* @param {EcwtFactory} ecwtFactory -
|
|
43
|
+
* @param {object} options -
|
|
44
|
+
* @param {string} options.token String representation of token.
|
|
45
|
+
* @param {Snowflake} options.snowflake -
|
|
46
|
+
* @param {number | null} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
47
|
+
* @param {D} options.data Data stored in token.
|
|
48
|
+
*/
|
|
49
|
+
constructor(ecwtFactory, options) {
|
|
50
|
+
this.token = options.token;
|
|
51
|
+
this.id = options.snowflake.toBase62();
|
|
52
|
+
this.snowflake = options.snowflake;
|
|
53
|
+
this.data = Object.freeze(options.data);
|
|
54
|
+
this.ecwtFactory = ecwtFactory;
|
|
55
|
+
this.ttl_initial = options.ttl_initial;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Unix timestamp of token expiration in seconds.
|
|
59
|
+
* @returns -
|
|
60
|
+
*/
|
|
61
|
+
get ts_expired() {
|
|
62
|
+
if (this.ttl_initial === null) return null;
|
|
63
|
+
return Math.floor(this.snowflake.timestamp / 1e3) + this.ttl_initial;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Actual time to live in seconds.
|
|
67
|
+
* @returns -
|
|
68
|
+
*/
|
|
69
|
+
getTTL() {
|
|
70
|
+
if (this.ttl_initial === null) return null;
|
|
71
|
+
return this.ttl_initial - Math.floor((Date.now() - this.snowflake.timestamp) / 1e3);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Revokes token.
|
|
75
|
+
* @returns {} -
|
|
76
|
+
*/
|
|
77
|
+
revoke() {
|
|
78
|
+
return this.ecwtFactory._revoke(this.id, this.snowflake.timestamp, this.ttl_initial);
|
|
79
|
+
}
|
|
139
80
|
};
|
|
140
81
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/utils.ts
|
|
84
|
+
const base62 = (0, base_x.default)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz");
|
|
144
85
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
* @param {string} property -
|
|
149
|
-
* @param {string} class_name -
|
|
150
|
-
* @param {string} package_name -
|
|
151
|
-
*/
|
|
152
|
-
constructor(property, class_name, package_name) {
|
|
153
|
-
super(
|
|
154
|
-
`Value ${property} must be an instance of ${class_name} from package "${package_name}". That error is probably caused by two separate installations of "${package_name}". Please, make sure that "${package_name}" in your project is matches "peerDependencies" of "ecwt" package.`
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/errors.ts
|
|
88
|
+
/** Error thrown when string token cannot be parsed to Ecwt. */
|
|
158
89
|
var EcwtParseError = class extends Error {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
90
|
+
constructor() {
|
|
91
|
+
super("Cannot parse data to Ecwt token.");
|
|
92
|
+
}
|
|
162
93
|
};
|
|
94
|
+
/** Error thrown when parsed Ecwt is invalid. */
|
|
163
95
|
var EcwtInvalidError = class extends Error {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
super();
|
|
170
|
-
this.ecwt = ecwt;
|
|
171
|
-
}
|
|
96
|
+
message = "Ecwt token is invalid.";
|
|
97
|
+
constructor(ecwt) {
|
|
98
|
+
super();
|
|
99
|
+
this.ecwt = ecwt;
|
|
100
|
+
}
|
|
172
101
|
};
|
|
102
|
+
/** Error thrown when parsed Ecwt is expired. */
|
|
173
103
|
var EcwtExpiredError = class extends EcwtInvalidError {
|
|
174
|
-
|
|
104
|
+
message = "Ecwt is expired.";
|
|
175
105
|
};
|
|
106
|
+
/** Error thrown when parsed Ecwt is revoked. */
|
|
176
107
|
var EcwtRevokedError = class extends EcwtInvalidError {
|
|
177
|
-
|
|
108
|
+
message = "Ecwt is revoked.";
|
|
178
109
|
};
|
|
179
110
|
|
|
180
|
-
|
|
181
|
-
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/factory.ts
|
|
113
|
+
const REDIS_PREFIX = "@ecwt:";
|
|
182
114
|
var EcwtFactory = class {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
if (typeof this.#validator === "function") {
|
|
336
|
-
try {
|
|
337
|
-
data = this.#validator(data);
|
|
338
|
-
} catch {
|
|
339
|
-
throw new EcwtParseError();
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
this.#setCache(
|
|
343
|
-
token,
|
|
344
|
-
{
|
|
345
|
-
snowflake,
|
|
346
|
-
ttl_initial,
|
|
347
|
-
data
|
|
348
|
-
}
|
|
349
|
-
);
|
|
350
|
-
} else {
|
|
351
|
-
({
|
|
352
|
-
snowflake,
|
|
353
|
-
ttl_initial,
|
|
354
|
-
data
|
|
355
|
-
} = cached_entry.value);
|
|
356
|
-
}
|
|
357
|
-
const ecwt = new Ecwt(
|
|
358
|
-
this,
|
|
359
|
-
{
|
|
360
|
-
token,
|
|
361
|
-
snowflake,
|
|
362
|
-
ttl_initial,
|
|
363
|
-
data
|
|
364
|
-
}
|
|
365
|
-
);
|
|
366
|
-
if (typeof ttl_initial === "number" && Number.isNaN(ttl_initial) !== true && snowflake.timestamp + ttl_initial * 1e3 < Date.now()) {
|
|
367
|
-
throw new EcwtExpiredError(ecwt);
|
|
368
|
-
}
|
|
369
|
-
if (this.#redisClient) {
|
|
370
|
-
const score = await this.#redisClient.ZSCORE(
|
|
371
|
-
this.#redis_key_revoked,
|
|
372
|
-
ecwt.id
|
|
373
|
-
);
|
|
374
|
-
if (score !== null) {
|
|
375
|
-
throw new EcwtRevokedError(ecwt);
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return ecwt;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Parses token without throwing errors.
|
|
382
|
-
* @async
|
|
383
|
-
* @param {string} token String representation of token.
|
|
384
|
-
* @returns {Promise<{ success: true, ecwt: Ecwt<D> } | { success: false, ecwt: Ecwt<D> | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
385
|
-
*/
|
|
386
|
-
async safeVerify(token) {
|
|
387
|
-
let ecwt = null;
|
|
388
|
-
try {
|
|
389
|
-
ecwt = await this.verify(token);
|
|
390
|
-
return {
|
|
391
|
-
success: true,
|
|
392
|
-
ecwt
|
|
393
|
-
};
|
|
394
|
-
} catch (error) {
|
|
395
|
-
if (error instanceof EcwtParseError) {
|
|
396
|
-
return {
|
|
397
|
-
success: false,
|
|
398
|
-
ecwt: null
|
|
399
|
-
};
|
|
400
|
-
}
|
|
401
|
-
if (error instanceof EcwtInvalidError) {
|
|
402
|
-
return {
|
|
403
|
-
success: false,
|
|
404
|
-
ecwt
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
throw error;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Revokes token.
|
|
412
|
-
* @async
|
|
413
|
-
* @param {object} options -
|
|
414
|
-
* @param {string} options.token_id -
|
|
415
|
-
* @param {number} options.ts_ms_created -
|
|
416
|
-
* @param {number | null} options.ttl_initial -
|
|
417
|
-
* @returns {Promise<void>} -
|
|
418
|
-
*/
|
|
419
|
-
async _revoke({
|
|
420
|
-
token_id,
|
|
421
|
-
ts_ms_created,
|
|
422
|
-
ttl_initial
|
|
423
|
-
}) {
|
|
424
|
-
if (this.#redisClient) {
|
|
425
|
-
ttl_initial ??= Number.MAX_SAFE_INTEGER;
|
|
426
|
-
const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
|
|
427
|
-
if (ts_ms_expired > Date.now()) {
|
|
428
|
-
await this.#redisClient.MULTI().addCommand([
|
|
429
|
-
"ZADD",
|
|
430
|
-
this.#redis_key_revoked,
|
|
431
|
-
String(ts_ms_expired),
|
|
432
|
-
token_id
|
|
433
|
-
]).addCommand([
|
|
434
|
-
"ZREMRANGEBYSCORE",
|
|
435
|
-
this.#redis_key_revoked,
|
|
436
|
-
"-inf",
|
|
437
|
-
String(Date.now())
|
|
438
|
-
]).EXEC();
|
|
439
|
-
}
|
|
440
|
-
} else {
|
|
441
|
-
console.warn("[ecwt] Redis client is not provided. Tokens cannot be revoked.");
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
/**
|
|
445
|
-
* Purges cache.
|
|
446
|
-
* @private
|
|
447
|
-
* @returns {void} -
|
|
448
|
-
*/
|
|
449
|
-
_purgeCache() {
|
|
450
|
-
this.#lruCache?.clear();
|
|
451
|
-
}
|
|
115
|
+
redisClient;
|
|
116
|
+
lruCache;
|
|
117
|
+
snowflakeFactory;
|
|
118
|
+
redis_key_revoked;
|
|
119
|
+
encryption_key;
|
|
120
|
+
validator;
|
|
121
|
+
cborEncoder = null;
|
|
122
|
+
constructor({ redisClient, lruCache, snowflakeFactory, options }) {
|
|
123
|
+
this.redisClient = redisClient;
|
|
124
|
+
this.lruCache = lruCache;
|
|
125
|
+
this.snowflakeFactory = snowflakeFactory;
|
|
126
|
+
this.redis_key_revoked = `${REDIS_PREFIX}${options.namespace}:revoked`;
|
|
127
|
+
this.encryption_key = options.key;
|
|
128
|
+
this.validator = options.validator;
|
|
129
|
+
if (options.senml_key_map) this.cborEncoder = new cbor_x.Encoder({ keyMap: options.senml_key_map });
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Creates new token.
|
|
133
|
+
* @async
|
|
134
|
+
* @param data - Data to be stored in token.
|
|
135
|
+
* @param options -
|
|
136
|
+
* @param options.ttl - Time to live in seconds. If not defined, token will never expire.
|
|
137
|
+
* @returns -
|
|
138
|
+
*/
|
|
139
|
+
async create(data, options = {}) {
|
|
140
|
+
if (typeof this.validator === "function") data = this.validator(data);
|
|
141
|
+
const ttl = options.ttl ?? null;
|
|
142
|
+
const snowflake = await this.snowflakeFactory.createSafe();
|
|
143
|
+
const payload = [
|
|
144
|
+
snowflake.toBuffer(),
|
|
145
|
+
ttl,
|
|
146
|
+
data
|
|
147
|
+
];
|
|
148
|
+
const token_raw = this.cborEncoder ? this.cborEncoder.encode(payload) : (0, cbor_x.encode)(payload);
|
|
149
|
+
const token_encrypted = await evilcrypt.v2.encrypt(token_raw, this.encryption_key);
|
|
150
|
+
const token = base62.encode(token_encrypted);
|
|
151
|
+
this.setCache(token, {
|
|
152
|
+
snowflake,
|
|
153
|
+
ttl_initial: ttl,
|
|
154
|
+
data
|
|
155
|
+
});
|
|
156
|
+
return new Ecwt(this, {
|
|
157
|
+
token,
|
|
158
|
+
snowflake,
|
|
159
|
+
ttl_initial: ttl,
|
|
160
|
+
data
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Sets data to cache.
|
|
165
|
+
* @param token - String representation of token.
|
|
166
|
+
* @param cache_value - Data to be stored in cache.
|
|
167
|
+
*/
|
|
168
|
+
setCache(token, cache_value) {
|
|
169
|
+
this.lruCache?.set(token, cache_value, cache_value.ttl_initial === null ? void 0 : { ttl: cache_value.ttl_initial * 1e3 });
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Parses token.
|
|
173
|
+
* @param token String representation of token.
|
|
174
|
+
* @returns -
|
|
175
|
+
*/
|
|
176
|
+
async verify(token) {
|
|
177
|
+
if (typeof token !== "string") throw new TypeError("Token must be a string.");
|
|
178
|
+
let snowflake;
|
|
179
|
+
let ttl_initial;
|
|
180
|
+
let data;
|
|
181
|
+
const cached_entry = this.lruCache?.info(token);
|
|
182
|
+
if (cached_entry === void 0) {
|
|
183
|
+
const token_encrypted = Buffer.from(base62.decode(token));
|
|
184
|
+
let token_raw;
|
|
185
|
+
try {
|
|
186
|
+
token_raw = await (0, evilcrypt.decrypt)(token_encrypted, this.encryption_key);
|
|
187
|
+
} catch {
|
|
188
|
+
throw new EcwtParseError();
|
|
189
|
+
}
|
|
190
|
+
const payload = this.cborEncoder ? this.cborEncoder.decode(token_raw) : (0, cbor_x.decode)(token_raw);
|
|
191
|
+
const [snowflake_buffer] = payload;
|
|
192
|
+
[, ttl_initial, data] = payload;
|
|
193
|
+
snowflake = this.snowflakeFactory.parse(snowflake_buffer);
|
|
194
|
+
if (typeof this.validator === "function") try {
|
|
195
|
+
data = this.validator(data);
|
|
196
|
+
} catch {
|
|
197
|
+
throw new EcwtParseError();
|
|
198
|
+
}
|
|
199
|
+
this.setCache(token, {
|
|
200
|
+
snowflake,
|
|
201
|
+
ttl_initial,
|
|
202
|
+
data
|
|
203
|
+
});
|
|
204
|
+
} else ({snowflake, ttl_initial, data} = cached_entry.value);
|
|
205
|
+
const ecwt = new Ecwt(this, {
|
|
206
|
+
token,
|
|
207
|
+
snowflake,
|
|
208
|
+
ttl_initial,
|
|
209
|
+
data
|
|
210
|
+
});
|
|
211
|
+
if (typeof ttl_initial === "number" && Number.isNaN(ttl_initial) !== true && snowflake.timestamp + ttl_initial * 1e3 < Date.now()) throw new EcwtExpiredError(ecwt);
|
|
212
|
+
if (this.redisClient) {
|
|
213
|
+
const score = await this.redisClient.ZSCORE(this.redis_key_revoked, ecwt.id);
|
|
214
|
+
if (score !== null) throw new EcwtRevokedError(ecwt);
|
|
215
|
+
}
|
|
216
|
+
return ecwt;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Parses token without throwing errors.
|
|
220
|
+
* @param token - String representation of token.
|
|
221
|
+
* @returns Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
222
|
+
*/
|
|
223
|
+
async safeVerify(token) {
|
|
224
|
+
let ecwt = null;
|
|
225
|
+
try {
|
|
226
|
+
ecwt = await this.verify(token);
|
|
227
|
+
return {
|
|
228
|
+
success: true,
|
|
229
|
+
ecwt
|
|
230
|
+
};
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (error instanceof EcwtParseError) return {
|
|
233
|
+
success: false,
|
|
234
|
+
ecwt: null
|
|
235
|
+
};
|
|
236
|
+
if (error instanceof EcwtInvalidError) return {
|
|
237
|
+
success: false,
|
|
238
|
+
ecwt
|
|
239
|
+
};
|
|
240
|
+
throw error;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Revokes token.
|
|
245
|
+
* @param token_id -
|
|
246
|
+
* @param ts_ms_created -
|
|
247
|
+
* @param ttl_initial -
|
|
248
|
+
* @returns -
|
|
249
|
+
*/
|
|
250
|
+
async _revoke(token_id, ts_ms_created, ttl_initial) {
|
|
251
|
+
if (this.redisClient) {
|
|
252
|
+
ttl_initial ??= Number.MAX_SAFE_INTEGER;
|
|
253
|
+
const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
|
|
254
|
+
if (ts_ms_expired > Date.now()) await this.redisClient.MULTI().ZADD(this.redis_key_revoked, {
|
|
255
|
+
score: ts_ms_expired,
|
|
256
|
+
value: token_id
|
|
257
|
+
}).ZREMRANGEBYSCORE(this.redis_key_revoked, "-inf", Date.now()).EXEC();
|
|
258
|
+
} else console.warn("[ecwt] Redis client is not provided. Tokens cannot be revoked.");
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Purges LRU cache.
|
|
262
|
+
* @returns {void} -
|
|
263
|
+
*/
|
|
264
|
+
_purgeCache() {
|
|
265
|
+
this.lruCache?.clear();
|
|
266
|
+
}
|
|
452
267
|
};
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
});
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
exports.Ecwt = Ecwt
|
|
271
|
+
exports.EcwtExpiredError = EcwtExpiredError
|
|
272
|
+
exports.EcwtFactory = EcwtFactory
|
|
273
|
+
exports.EcwtInvalidError = EcwtInvalidError
|
|
274
|
+
exports.EcwtParseError = EcwtParseError
|
|
275
|
+
exports.EcwtRevokedError = EcwtRevokedError
|