ecwt 0.2.0-beta.2 → 0.2.0-beta.4
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 +26 -2
- package/dist/ecwt.cjs +49 -9
- package/package.json +3 -3
- package/src/factory.js +70 -13
- package/src/utils/errors.js +12 -0
package/README.md
CHANGED
|
@@ -76,7 +76,8 @@ In our example, we use [valibot](https://valibot.dev) library.
|
|
|
76
76
|
```javascript
|
|
77
77
|
import * as v from 'valibot';
|
|
78
78
|
|
|
79
|
-
const schema =
|
|
79
|
+
const schema = v.parse.bind(
|
|
80
|
+
null,
|
|
80
81
|
v.object({
|
|
81
82
|
user_id: v.number([
|
|
82
83
|
v.maxValue(10),
|
|
@@ -85,7 +86,6 @@ const schema = (value) => v.parse(
|
|
|
85
86
|
v.maxLength(10),
|
|
86
87
|
]),
|
|
87
88
|
}),
|
|
88
|
-
value,
|
|
89
89
|
);
|
|
90
90
|
```
|
|
91
91
|
|
|
@@ -197,6 +197,30 @@ If the token is invalid, throws `EcwtInvalidError` which contains `Ecwt` instanc
|
|
|
197
197
|
const ecwt = await ecwtFactory.verify(token);
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
+
#### Class method `safeVerify`
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
safeVerify(
|
|
204
|
+
token: string,
|
|
205
|
+
): Promise<{
|
|
206
|
+
success: boolean,
|
|
207
|
+
ecwt: Ecwt | null,
|
|
208
|
+
}>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The same method as `verify`, but does not throw an error if the token is invalid, expired or revoked.
|
|
212
|
+
|
|
213
|
+
Property `success` is `true` if the token is valid.
|
|
214
|
+
|
|
215
|
+
Property `ecwt` is `null` if the token cannot be parsed, otherwise it contains `Ecwt` instance.
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
const {
|
|
219
|
+
success,
|
|
220
|
+
ecwt,
|
|
221
|
+
} = await ecwtFactory.safeVerify(token);
|
|
222
|
+
```
|
|
223
|
+
|
|
200
224
|
### `Ecwt`
|
|
201
225
|
|
|
202
226
|
Represents the token. Its counstructor cannot be called by the user.
|
package/dist/ecwt.cjs
CHANGED
|
@@ -229,17 +229,17 @@ var EcwtFactory = class {
|
|
|
229
229
|
* @param {{ [key: string]: number }} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
230
230
|
*/
|
|
231
231
|
constructor({
|
|
232
|
-
redisClient: redisClient2
|
|
233
|
-
lruCache
|
|
232
|
+
redisClient: redisClient2,
|
|
233
|
+
lruCache,
|
|
234
234
|
snowflakeFactory,
|
|
235
235
|
options: {
|
|
236
|
-
namespace
|
|
236
|
+
namespace,
|
|
237
237
|
key,
|
|
238
238
|
validator,
|
|
239
239
|
senml_key_map
|
|
240
240
|
}
|
|
241
241
|
}) {
|
|
242
|
-
if (redisClient2 !==
|
|
242
|
+
if (redisClient2 !== void 0 && (redisClient2.constructor.name !== redis_client_constructor_name || getAllKeysList(redisClient2) !== redis_client_keys)) {
|
|
243
243
|
throw new InvalidPackageInstanceError(
|
|
244
244
|
"redisClient",
|
|
245
245
|
"Commander extends RedisClient",
|
|
@@ -247,7 +247,7 @@ var EcwtFactory = class {
|
|
|
247
247
|
);
|
|
248
248
|
}
|
|
249
249
|
this.#redisClient = redisClient2;
|
|
250
|
-
if (lruCache !==
|
|
250
|
+
if (lruCache !== void 0 && lruCache instanceof import_lru_cache.LRUCache !== true) {
|
|
251
251
|
throw new InvalidPackageInstanceError(
|
|
252
252
|
"lruCache",
|
|
253
253
|
"LRUCache",
|
|
@@ -277,11 +277,11 @@ var EcwtFactory = class {
|
|
|
277
277
|
* @async
|
|
278
278
|
* @param {object} data Data to be stored in token.
|
|
279
279
|
* @param {object} [options] -
|
|
280
|
-
* @param {number} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
280
|
+
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
281
281
|
* @returns {Promise<Ecwt>} -
|
|
282
282
|
*/
|
|
283
283
|
async create(data, {
|
|
284
|
-
ttl
|
|
284
|
+
ttl = null
|
|
285
285
|
} = {}) {
|
|
286
286
|
if (typeof this.#validator === "function") {
|
|
287
287
|
data = this.#validator(data);
|
|
@@ -319,6 +319,11 @@ var EcwtFactory = class {
|
|
|
319
319
|
}
|
|
320
320
|
);
|
|
321
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* Sets data to cache.
|
|
324
|
+
* @param {string} token String representation of token.
|
|
325
|
+
* @param {object} data Data to be stored in cache.
|
|
326
|
+
*/
|
|
322
327
|
#setCache(token, data) {
|
|
323
328
|
this.#lruCache?.set(
|
|
324
329
|
token,
|
|
@@ -410,6 +415,36 @@ var EcwtFactory = class {
|
|
|
410
415
|
}
|
|
411
416
|
return ecwt;
|
|
412
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* Parses token without throwing errors.
|
|
420
|
+
* @async
|
|
421
|
+
* @param {string} token String representation of token.
|
|
422
|
+
* @returns {Promise<{ success: boolean, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
423
|
+
*/
|
|
424
|
+
async safeVerify(token) {
|
|
425
|
+
let ecwt = null;
|
|
426
|
+
try {
|
|
427
|
+
ecwt = await this.verify(token);
|
|
428
|
+
return {
|
|
429
|
+
success: true,
|
|
430
|
+
ecwt
|
|
431
|
+
};
|
|
432
|
+
} catch (error) {
|
|
433
|
+
if (error instanceof EcwtParseError) {
|
|
434
|
+
return {
|
|
435
|
+
success: false,
|
|
436
|
+
ecwt: null
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
if (error instanceof EcwtInvalidError) {
|
|
440
|
+
return {
|
|
441
|
+
success: false,
|
|
442
|
+
ecwt
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
throw error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
413
448
|
/**
|
|
414
449
|
* Revokes token.
|
|
415
450
|
* @async
|
|
@@ -428,12 +463,17 @@ var EcwtFactory = class {
|
|
|
428
463
|
ttl_initial = ttl_initial ?? Number.MAX_SAFE_INTEGER;
|
|
429
464
|
const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
|
|
430
465
|
if (ts_ms_expired > Date.now()) {
|
|
431
|
-
await this.#redisClient.
|
|
466
|
+
await this.#redisClient.MULTI().addCommand([
|
|
432
467
|
"ZADD",
|
|
433
468
|
this.#redis_keys.revoked,
|
|
434
469
|
String(ts_ms_expired),
|
|
435
470
|
token_id
|
|
436
|
-
])
|
|
471
|
+
]).addCommand([
|
|
472
|
+
"ZREMRANGEBYSCORE",
|
|
473
|
+
this.#redis_keys.revoked,
|
|
474
|
+
"-inf",
|
|
475
|
+
String(Date.now())
|
|
476
|
+
]).EXEC();
|
|
437
477
|
}
|
|
438
478
|
} else {
|
|
439
479
|
console.warn("[ecwt] Redis client is not provided. Tokens cannot be revoked.");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ecwt",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.4",
|
|
4
4
|
"description": "Encrypted CBOR-encoded Web Token",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"type": "module",
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
"eslint-plugin-node": "11.1.0",
|
|
33
33
|
"eslint-plugin-promise": "6.1.1",
|
|
34
34
|
"eslint-plugin-unicorn": "47.0.0",
|
|
35
|
-
"valibot": "
|
|
36
|
-
"vitest": "1.
|
|
35
|
+
"valibot": "0.33",
|
|
36
|
+
"vitest": "1.6"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"test": "bun run redis-up && bun test && npm run test-node",
|
package/src/factory.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
|
|
2
|
+
// @ts-check
|
|
3
|
+
|
|
2
4
|
import { SnowflakeFactory } from '@kirick/snowflake';
|
|
3
5
|
import {
|
|
4
6
|
Encoder as CborEncoder,
|
|
@@ -13,6 +15,7 @@ import { Ecwt } from './token.js';
|
|
|
13
15
|
import { base62 } from './utils/base62.js';
|
|
14
16
|
import {
|
|
15
17
|
InvalidPackageInstanceError,
|
|
18
|
+
EcwtInvalidError,
|
|
16
19
|
EcwtExpiredError,
|
|
17
20
|
EcwtRevokedError,
|
|
18
21
|
EcwtParseError } from './utils/errors.js';
|
|
@@ -57,18 +60,18 @@ export class EcwtFactory {
|
|
|
57
60
|
* @param {{ [key: string]: number }} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
58
61
|
*/
|
|
59
62
|
constructor({
|
|
60
|
-
redisClient
|
|
61
|
-
lruCache
|
|
63
|
+
redisClient,
|
|
64
|
+
lruCache,
|
|
62
65
|
snowflakeFactory,
|
|
63
66
|
options: {
|
|
64
|
-
namespace
|
|
67
|
+
namespace,
|
|
65
68
|
key,
|
|
66
69
|
validator,
|
|
67
70
|
senml_key_map,
|
|
68
71
|
},
|
|
69
72
|
}) {
|
|
70
73
|
if (
|
|
71
|
-
redisClient !==
|
|
74
|
+
redisClient !== undefined
|
|
72
75
|
&& (
|
|
73
76
|
redisClient.constructor.name !== redis_client_constructor_name
|
|
74
77
|
|| getAllKeysList(redisClient) !== redis_client_keys
|
|
@@ -83,7 +86,7 @@ export class EcwtFactory {
|
|
|
83
86
|
this.#redisClient = redisClient;
|
|
84
87
|
|
|
85
88
|
if (
|
|
86
|
-
lruCache !==
|
|
89
|
+
lruCache !== undefined
|
|
87
90
|
&& lruCache instanceof LRUCache !== true
|
|
88
91
|
) {
|
|
89
92
|
throw new InvalidPackageInstanceError(
|
|
@@ -121,13 +124,13 @@ export class EcwtFactory {
|
|
|
121
124
|
* @async
|
|
122
125
|
* @param {object} data Data to be stored in token.
|
|
123
126
|
* @param {object} [options] -
|
|
124
|
-
* @param {number} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
127
|
+
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
125
128
|
* @returns {Promise<Ecwt>} -
|
|
126
129
|
*/
|
|
127
130
|
async create(
|
|
128
131
|
data,
|
|
129
132
|
{
|
|
130
|
-
ttl,
|
|
133
|
+
ttl = null,
|
|
131
134
|
} = {},
|
|
132
135
|
) {
|
|
133
136
|
if (typeof this.#validator === 'function') {
|
|
@@ -180,6 +183,11 @@ export class EcwtFactory {
|
|
|
180
183
|
);
|
|
181
184
|
}
|
|
182
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Sets data to cache.
|
|
188
|
+
* @param {string} token String representation of token.
|
|
189
|
+
* @param {object} data Data to be stored in cache.
|
|
190
|
+
*/
|
|
183
191
|
#setCache(token, data) {
|
|
184
192
|
this.#lruCache?.set(
|
|
185
193
|
token,
|
|
@@ -299,6 +307,41 @@ export class EcwtFactory {
|
|
|
299
307
|
return ecwt;
|
|
300
308
|
}
|
|
301
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Parses token without throwing errors.
|
|
312
|
+
* @async
|
|
313
|
+
* @param {string} token String representation of token.
|
|
314
|
+
* @returns {Promise<{ success: boolean, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
315
|
+
*/
|
|
316
|
+
async safeVerify(token) {
|
|
317
|
+
let ecwt = null;
|
|
318
|
+
try {
|
|
319
|
+
ecwt = await this.verify(token);
|
|
320
|
+
|
|
321
|
+
return {
|
|
322
|
+
success: true,
|
|
323
|
+
ecwt,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
if (error instanceof EcwtParseError) {
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
ecwt: null,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (error instanceof EcwtInvalidError) {
|
|
335
|
+
return {
|
|
336
|
+
success: false,
|
|
337
|
+
ecwt,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
throw error;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
302
345
|
/**
|
|
303
346
|
* Revokes token.
|
|
304
347
|
* @async
|
|
@@ -318,12 +361,26 @@ export class EcwtFactory {
|
|
|
318
361
|
|
|
319
362
|
const ts_ms_expired = ts_ms_created + (ttl_initial * 1000);
|
|
320
363
|
if (ts_ms_expired > Date.now()) {
|
|
321
|
-
await this.#redisClient.sendCommand([
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
]);
|
|
364
|
+
// await this.#redisClient.sendCommand([
|
|
365
|
+
// 'ZADD',
|
|
366
|
+
// this.#redis_keys.revoked,
|
|
367
|
+
// String(ts_ms_expired),
|
|
368
|
+
// token_id,
|
|
369
|
+
// ]);
|
|
370
|
+
await this.#redisClient.MULTI()
|
|
371
|
+
.addCommand([
|
|
372
|
+
'ZADD',
|
|
373
|
+
this.#redis_keys.revoked,
|
|
374
|
+
String(ts_ms_expired),
|
|
375
|
+
token_id,
|
|
376
|
+
])
|
|
377
|
+
.addCommand([
|
|
378
|
+
'ZREMRANGEBYSCORE',
|
|
379
|
+
this.#redis_keys.revoked,
|
|
380
|
+
'-inf',
|
|
381
|
+
String(Date.now()),
|
|
382
|
+
])
|
|
383
|
+
.EXEC();
|
|
327
384
|
}
|
|
328
385
|
}
|
|
329
386
|
else {
|
package/src/utils/errors.js
CHANGED
|
@@ -5,12 +5,18 @@ export class InvalidPackageInstanceError extends TypeError {
|
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Error thrown when string token cannot be parsed to Ecwt.
|
|
10
|
+
*/
|
|
8
11
|
export class EcwtParseError extends Error {
|
|
9
12
|
constructor() {
|
|
10
13
|
super('Cannot parse data to Ecwt token.');
|
|
11
14
|
}
|
|
12
15
|
}
|
|
13
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Error thrown when parsed Ecwt is invalid.
|
|
19
|
+
*/
|
|
14
20
|
export class EcwtInvalidError extends Error {
|
|
15
21
|
constructor(ecwt) {
|
|
16
22
|
super('Ecwt token is invalid.');
|
|
@@ -19,6 +25,9 @@ export class EcwtInvalidError extends Error {
|
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Error thrown when parsed Ecwt is expired.
|
|
30
|
+
*/
|
|
22
31
|
export class EcwtExpiredError extends EcwtInvalidError {
|
|
23
32
|
constructor(ecwt) {
|
|
24
33
|
super();
|
|
@@ -28,6 +37,9 @@ export class EcwtExpiredError extends EcwtInvalidError {
|
|
|
28
37
|
}
|
|
29
38
|
}
|
|
30
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Error thrown when parsed Ecwt is revoked.
|
|
42
|
+
*/
|
|
31
43
|
export class EcwtRevokedError extends EcwtInvalidError {
|
|
32
44
|
constructor(ecwt) {
|
|
33
45
|
super();
|