ecwt 0.2.0-beta.5 → 0.2.1-beta.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/dist/{ecwt.cjs → main.cjs} +34 -54
- package/package.json +19 -17
- package/src/factory.js +26 -14
- package/src/main.js +0 -2
- package/src/token.js +10 -42
- package/src/utils/base62.js +0 -2
- package/src/utils/errors.js +16 -14
- package/src/utils/time.js +0 -2
- package/tsconfig.json +19 -0
- package/types/factory.d.ts +95 -0
- package/types/main.d.ts +3 -0
- package/types/token.d.ts +65 -0
- package/types/utils/base62.d.ts +2 -0
- package/types/utils/errors.d.ts +38 -0
- package/types/utils/time.d.ts +6 -0
|
@@ -51,21 +51,7 @@ function toSeconds(value) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
// src/token.js
|
|
54
|
-
function assign(target, key, value) {
|
|
55
|
-
Object.defineProperty(
|
|
56
|
-
target,
|
|
57
|
-
key,
|
|
58
|
-
{
|
|
59
|
-
value,
|
|
60
|
-
enumerable: true,
|
|
61
|
-
writable: false,
|
|
62
|
-
configurable: false
|
|
63
|
-
}
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
54
|
var Ecwt = class {
|
|
67
|
-
#ecwtFactory;
|
|
68
|
-
#ttl_initial;
|
|
69
55
|
/**
|
|
70
56
|
* Token string representation.
|
|
71
57
|
* @type {string}
|
|
@@ -96,6 +82,10 @@ var Ecwt = class {
|
|
|
96
82
|
* @readonly
|
|
97
83
|
*/
|
|
98
84
|
data;
|
|
85
|
+
/** @type {EcwtFactory} */
|
|
86
|
+
#ecwtFactory;
|
|
87
|
+
/** @type {number} */
|
|
88
|
+
#ttl_initial;
|
|
99
89
|
/**
|
|
100
90
|
* @param {EcwtFactory} ecwtFactory -
|
|
101
91
|
* @param {object} options -
|
|
@@ -112,23 +102,11 @@ var Ecwt = class {
|
|
|
112
102
|
}) {
|
|
113
103
|
this.#ecwtFactory = ecwtFactory;
|
|
114
104
|
this.#ttl_initial = ttl_initial;
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
assign(this, "snowflake", snowflake);
|
|
122
|
-
assign(
|
|
123
|
-
this,
|
|
124
|
-
"ts_expired",
|
|
125
|
-
this.#getTimestampExpired()
|
|
126
|
-
);
|
|
127
|
-
assign(
|
|
128
|
-
this,
|
|
129
|
-
"data",
|
|
130
|
-
Object.freeze(data)
|
|
131
|
-
);
|
|
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);
|
|
132
110
|
}
|
|
133
111
|
#getTimestampExpired() {
|
|
134
112
|
if (this.#ttl_initial === null) {
|
|
@@ -166,6 +144,11 @@ var base62 = (0, import_base_x.default)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabc
|
|
|
166
144
|
|
|
167
145
|
// src/utils/errors.js
|
|
168
146
|
var InvalidPackageInstanceError = class extends TypeError {
|
|
147
|
+
/**
|
|
148
|
+
* @param {string} property -
|
|
149
|
+
* @param {string} class_name -
|
|
150
|
+
* @param {string} package_name -
|
|
151
|
+
*/
|
|
169
152
|
constructor(property, class_name, package_name) {
|
|
170
153
|
super(`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.`);
|
|
171
154
|
}
|
|
@@ -176,24 +159,20 @@ var EcwtParseError = class extends Error {
|
|
|
176
159
|
}
|
|
177
160
|
};
|
|
178
161
|
var EcwtInvalidError = class extends Error {
|
|
162
|
+
message = "Ecwt token is invalid.";
|
|
163
|
+
/**
|
|
164
|
+
* @param {Ecwt} ecwt -
|
|
165
|
+
*/
|
|
179
166
|
constructor(ecwt) {
|
|
180
|
-
super(
|
|
167
|
+
super();
|
|
181
168
|
this.ecwt = ecwt;
|
|
182
169
|
}
|
|
183
170
|
};
|
|
184
171
|
var EcwtExpiredError = class extends EcwtInvalidError {
|
|
185
|
-
|
|
186
|
-
super();
|
|
187
|
-
this.ecwt = ecwt;
|
|
188
|
-
this.message = "Ecwt is expired.";
|
|
189
|
-
}
|
|
172
|
+
message = "Ecwt is expired.";
|
|
190
173
|
};
|
|
191
174
|
var EcwtRevokedError = class extends EcwtInvalidError {
|
|
192
|
-
|
|
193
|
-
super();
|
|
194
|
-
this.ecwt = ecwt;
|
|
195
|
-
this.message = "Ecwt is revoked.";
|
|
196
|
-
}
|
|
175
|
+
message = "Ecwt is revoked.";
|
|
197
176
|
};
|
|
198
177
|
|
|
199
178
|
// src/factory.js
|
|
@@ -212,21 +191,22 @@ var EcwtFactory = class {
|
|
|
212
191
|
#redisClient;
|
|
213
192
|
#lruCache;
|
|
214
193
|
#snowflakeFactory;
|
|
215
|
-
#
|
|
194
|
+
#redis_key_revoked;
|
|
216
195
|
#encryption_key;
|
|
217
196
|
#validator;
|
|
218
|
-
|
|
197
|
+
/** @type {CborEncoder | null} */
|
|
198
|
+
#cborEncoder = null;
|
|
219
199
|
/**
|
|
220
200
|
*
|
|
221
201
|
* @param {object} param0 -
|
|
222
202
|
* @param {import('redis').RedisClientType} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
223
|
-
* @param {LRUCache} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
203
|
+
* @param {LRUCache<string, CacheValue>} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
224
204
|
* @param {SnowflakeFactory} param0.snowflakeFactory SnowflakeFactory instance.
|
|
225
205
|
* @param {object} param0.options -
|
|
226
206
|
* @param {string} [param0.options.namespace] Namespace for Redis keys.
|
|
227
207
|
* @param {Buffer} param0.options.key Encryption key, 64 bytes
|
|
228
208
|
* @param {(value: any) => any} [param0.options.validator] Validator for token data. Should return validated value or throw an error.
|
|
229
|
-
* @param {
|
|
209
|
+
* @param {Record<string, number>} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
230
210
|
*/
|
|
231
211
|
constructor({
|
|
232
212
|
redisClient: redisClient2,
|
|
@@ -263,7 +243,7 @@ var EcwtFactory = class {
|
|
|
263
243
|
);
|
|
264
244
|
}
|
|
265
245
|
this.#snowflakeFactory = snowflakeFactory;
|
|
266
|
-
this.#
|
|
246
|
+
this.#redis_key_revoked = `${REDIS_PREFIX}${namespace}:revoked`;
|
|
267
247
|
this.#encryption_key = key;
|
|
268
248
|
this.#validator = validator;
|
|
269
249
|
if (senml_key_map) {
|
|
@@ -322,14 +302,14 @@ var EcwtFactory = class {
|
|
|
322
302
|
/**
|
|
323
303
|
* Sets data to cache.
|
|
324
304
|
* @param {string} token String representation of token.
|
|
325
|
-
* @param {
|
|
305
|
+
* @param {CacheValue} cache_value Data to be stored in cache.
|
|
326
306
|
*/
|
|
327
|
-
#setCache(token,
|
|
307
|
+
#setCache(token, cache_value) {
|
|
328
308
|
this.#lruCache?.set(
|
|
329
309
|
token,
|
|
330
|
-
|
|
310
|
+
cache_value,
|
|
331
311
|
{
|
|
332
|
-
ttl:
|
|
312
|
+
ttl: cache_value.ttl_initial * 1e3
|
|
333
313
|
}
|
|
334
314
|
);
|
|
335
315
|
}
|
|
@@ -406,7 +386,7 @@ var EcwtFactory = class {
|
|
|
406
386
|
}
|
|
407
387
|
if (this.#redisClient) {
|
|
408
388
|
const score = await this.#redisClient.ZSCORE(
|
|
409
|
-
this.#
|
|
389
|
+
this.#redis_key_revoked,
|
|
410
390
|
ecwt.id
|
|
411
391
|
);
|
|
412
392
|
if (score !== null) {
|
|
@@ -465,12 +445,12 @@ var EcwtFactory = class {
|
|
|
465
445
|
if (ts_ms_expired > Date.now()) {
|
|
466
446
|
await this.#redisClient.MULTI().addCommand([
|
|
467
447
|
"ZADD",
|
|
468
|
-
this.#
|
|
448
|
+
this.#redis_key_revoked,
|
|
469
449
|
String(ts_ms_expired),
|
|
470
450
|
token_id
|
|
471
451
|
]).addCommand([
|
|
472
452
|
"ZREMRANGEBYSCORE",
|
|
473
|
-
this.#
|
|
453
|
+
this.#redis_key_revoked,
|
|
474
454
|
"-inf",
|
|
475
455
|
String(Date.now())
|
|
476
456
|
]).EXEC();
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ecwt",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1-beta.1",
|
|
4
4
|
"description": "Encrypted CBOR-encoded Web Token",
|
|
5
|
-
"main": "src/main.js",
|
|
6
5
|
"type": "module",
|
|
6
|
+
"main": "src/main.js",
|
|
7
|
+
"types": "types/main.d.ts",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
9
10
|
"import": "./src/main.js",
|
|
10
|
-
"require": "./dist/
|
|
11
|
+
"require": "./dist/main.cjs"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
14
|
"engines": {
|
|
@@ -16,35 +17,36 @@
|
|
|
16
17
|
"dependencies": {
|
|
17
18
|
"base-x": "4.0.0",
|
|
18
19
|
"cbor-x": "1.5.6",
|
|
19
|
-
"evilcrypt": "0.2.
|
|
20
|
+
"evilcrypt": "0.2.1-beta.1"
|
|
20
21
|
},
|
|
21
22
|
"peerDependencies": {
|
|
22
|
-
"@kirick/snowflake": "^0.2.
|
|
23
|
+
"@kirick/snowflake": "^0.2.2-beta.2",
|
|
23
24
|
"lru-cache": "^9 || ^10",
|
|
24
25
|
"redis": "^4"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@babel/eslint-parser": "7.21.8",
|
|
29
|
+
"@types/node": "^20.14.9",
|
|
28
30
|
"eslint": "8.41.0",
|
|
29
31
|
"eslint-config-xo": "0.43.1",
|
|
30
32
|
"eslint-plugin-import": "2.27.5",
|
|
31
|
-
"eslint-plugin-jsdoc": "46.
|
|
33
|
+
"eslint-plugin-jsdoc": "46.5.0",
|
|
32
34
|
"eslint-plugin-node": "11.1.0",
|
|
33
35
|
"eslint-plugin-promise": "6.1.1",
|
|
34
36
|
"eslint-plugin-unicorn": "47.0.0",
|
|
35
|
-
"valibot": "0.33",
|
|
36
|
-
"vitest": "1.6"
|
|
37
|
+
"valibot": "^0.33",
|
|
38
|
+
"vitest": "^1.6"
|
|
37
39
|
},
|
|
38
40
|
"scripts": {
|
|
39
|
-
"test": "bun run redis
|
|
40
|
-
"test
|
|
41
|
-
"
|
|
42
|
-
"build": "
|
|
43
|
-
"build
|
|
44
|
-
"
|
|
45
|
-
"npm
|
|
46
|
-
"redis
|
|
47
|
-
"redis
|
|
41
|
+
"test": "export REDIS_PORT=16274 ; bun run redis:up && npm run test:vitest && bun test --coverage ; bun run redis:down",
|
|
42
|
+
"test:vitest": "vitest run --no-file-parallelism",
|
|
43
|
+
"build": "bun run build:types && bun run build:cjs",
|
|
44
|
+
"build:cjs": "bunx esbuild --bundle --platform=node --format=cjs --packages=external --outfile=dist/main.cjs src/main.js",
|
|
45
|
+
"build:types": "bunx tsc --skipLibCheck --declaration --emitDeclarationOnly --outDir types",
|
|
46
|
+
"lint": "eslint . && bunx tsc --skipLibCheck --noemit",
|
|
47
|
+
"publish:npm": "bun run build && bun run lint && bun run test && npm publish",
|
|
48
|
+
"redis:up": "docker ps | grep test-ecwt >/dev/null || docker run --rm -d -p $REDIS_PORT:6379 --name test-ecwt redis:7-alpine",
|
|
49
|
+
"redis:down": "docker stop test-ecwt || true"
|
|
48
50
|
},
|
|
49
51
|
"repository": {
|
|
50
52
|
"type": "git",
|
package/src/factory.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('@kirick/snowflake').Snowflake} Snowflake
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {object} CacheValue
|
|
7
|
+
* @property {Snowflake} snowflake -
|
|
8
|
+
* @property {number} ttl_initial -
|
|
9
|
+
* @property {Record<string, any>} data -
|
|
10
|
+
*/
|
|
3
11
|
|
|
4
12
|
import { SnowflakeFactory } from '@kirick/snowflake';
|
|
5
13
|
import {
|
|
@@ -22,7 +30,10 @@ import {
|
|
|
22
30
|
|
|
23
31
|
const REDIS_PREFIX = '@ecwt:';
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
/**
|
|
34
|
+
* @param {object} value -
|
|
35
|
+
* @returns {string} -
|
|
36
|
+
*/
|
|
26
37
|
function getAllKeysList(value) {
|
|
27
38
|
const keys = [];
|
|
28
39
|
// eslint-disable-next-line guard-for-in
|
|
@@ -41,23 +52,24 @@ export class EcwtFactory {
|
|
|
41
52
|
#lruCache;
|
|
42
53
|
#snowflakeFactory;
|
|
43
54
|
|
|
44
|
-
#
|
|
55
|
+
#redis_key_revoked;
|
|
45
56
|
#encryption_key;
|
|
46
57
|
|
|
47
58
|
#validator;
|
|
48
|
-
|
|
59
|
+
/** @type {CborEncoder | null} */
|
|
60
|
+
#cborEncoder = null;
|
|
49
61
|
|
|
50
62
|
/**
|
|
51
63
|
*
|
|
52
64
|
* @param {object} param0 -
|
|
53
65
|
* @param {import('redis').RedisClientType} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
54
|
-
* @param {LRUCache} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
66
|
+
* @param {LRUCache<string, CacheValue>} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
55
67
|
* @param {SnowflakeFactory} param0.snowflakeFactory SnowflakeFactory instance.
|
|
56
68
|
* @param {object} param0.options -
|
|
57
69
|
* @param {string} [param0.options.namespace] Namespace for Redis keys.
|
|
58
70
|
* @param {Buffer} param0.options.key Encryption key, 64 bytes
|
|
59
71
|
* @param {(value: any) => any} [param0.options.validator] Validator for token data. Should return validated value or throw an error.
|
|
60
|
-
* @param {
|
|
72
|
+
* @param {Record<string, number>} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
61
73
|
*/
|
|
62
74
|
constructor({
|
|
63
75
|
redisClient,
|
|
@@ -106,7 +118,7 @@ export class EcwtFactory {
|
|
|
106
118
|
}
|
|
107
119
|
this.#snowflakeFactory = snowflakeFactory;
|
|
108
120
|
|
|
109
|
-
this.#
|
|
121
|
+
this.#redis_key_revoked = `${REDIS_PREFIX}${namespace}:revoked`;
|
|
110
122
|
|
|
111
123
|
this.#encryption_key = key;
|
|
112
124
|
|
|
@@ -186,14 +198,14 @@ export class EcwtFactory {
|
|
|
186
198
|
/**
|
|
187
199
|
* Sets data to cache.
|
|
188
200
|
* @param {string} token String representation of token.
|
|
189
|
-
* @param {
|
|
201
|
+
* @param {CacheValue} cache_value Data to be stored in cache.
|
|
190
202
|
*/
|
|
191
|
-
#setCache(token,
|
|
203
|
+
#setCache(token, cache_value) {
|
|
192
204
|
this.#lruCache?.set(
|
|
193
205
|
token,
|
|
194
|
-
|
|
206
|
+
cache_value,
|
|
195
207
|
{
|
|
196
|
-
ttl:
|
|
208
|
+
ttl: cache_value.ttl_initial * 1000,
|
|
197
209
|
},
|
|
198
210
|
);
|
|
199
211
|
}
|
|
@@ -296,7 +308,7 @@ export class EcwtFactory {
|
|
|
296
308
|
|
|
297
309
|
if (this.#redisClient) {
|
|
298
310
|
const score = await this.#redisClient.ZSCORE(
|
|
299
|
-
this.#
|
|
311
|
+
this.#redis_key_revoked,
|
|
300
312
|
ecwt.id,
|
|
301
313
|
);
|
|
302
314
|
if (score !== null) {
|
|
@@ -370,13 +382,13 @@ export class EcwtFactory {
|
|
|
370
382
|
await this.#redisClient.MULTI()
|
|
371
383
|
.addCommand([
|
|
372
384
|
'ZADD',
|
|
373
|
-
this.#
|
|
385
|
+
this.#redis_key_revoked,
|
|
374
386
|
String(ts_ms_expired),
|
|
375
387
|
token_id,
|
|
376
388
|
])
|
|
377
389
|
.addCommand([
|
|
378
390
|
'ZREMRANGEBYSCORE',
|
|
379
|
-
this.#
|
|
391
|
+
this.#redis_key_revoked,
|
|
380
392
|
'-inf',
|
|
381
393
|
String(Date.now()),
|
|
382
394
|
])
|
package/src/main.js
CHANGED
package/src/token.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
// @ts-check
|
|
3
|
-
|
|
4
2
|
import { toSeconds } from './utils/time.js';
|
|
5
3
|
|
|
6
4
|
/**
|
|
@@ -8,29 +6,7 @@ import { toSeconds } from './utils/time.js';
|
|
|
8
6
|
* @typedef {import('./factory.js').EcwtFactory} EcwtFactory
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
|
-
/**
|
|
12
|
-
* Assigns property to object.
|
|
13
|
-
* @param {object} target -
|
|
14
|
-
* @param {string} key -
|
|
15
|
-
* @param {any} value -
|
|
16
|
-
*/
|
|
17
|
-
function assign(target, key, value) {
|
|
18
|
-
Object.defineProperty(
|
|
19
|
-
target,
|
|
20
|
-
key,
|
|
21
|
-
{
|
|
22
|
-
value,
|
|
23
|
-
enumerable: true,
|
|
24
|
-
writable: false,
|
|
25
|
-
configurable: false,
|
|
26
|
-
},
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
9
|
export class Ecwt {
|
|
31
|
-
#ecwtFactory;
|
|
32
|
-
#ttl_initial;
|
|
33
|
-
|
|
34
10
|
/**
|
|
35
11
|
* Token string representation.
|
|
36
12
|
* @type {string}
|
|
@@ -62,6 +38,11 @@ export class Ecwt {
|
|
|
62
38
|
*/
|
|
63
39
|
data;
|
|
64
40
|
|
|
41
|
+
/** @type {EcwtFactory} */
|
|
42
|
+
#ecwtFactory;
|
|
43
|
+
/** @type {number} */
|
|
44
|
+
#ttl_initial;
|
|
45
|
+
|
|
65
46
|
/**
|
|
66
47
|
* @param {EcwtFactory} ecwtFactory -
|
|
67
48
|
* @param {object} options -
|
|
@@ -80,26 +61,13 @@ export class Ecwt {
|
|
|
80
61
|
},
|
|
81
62
|
) {
|
|
82
63
|
this.#ecwtFactory = ecwtFactory;
|
|
83
|
-
|
|
84
64
|
this.#ttl_initial = ttl_initial;
|
|
85
65
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
);
|
|
92
|
-
assign(this, 'snowflake', snowflake);
|
|
93
|
-
assign(
|
|
94
|
-
this,
|
|
95
|
-
'ts_expired',
|
|
96
|
-
this.#getTimestampExpired(),
|
|
97
|
-
);
|
|
98
|
-
assign(
|
|
99
|
-
this,
|
|
100
|
-
'data',
|
|
101
|
-
Object.freeze(data),
|
|
102
|
-
);
|
|
66
|
+
this.token = token;
|
|
67
|
+
this.id = snowflake.toBase62();
|
|
68
|
+
this.snowflake = snowflake;
|
|
69
|
+
this.ts_expired = this.#getTimestampExpired();
|
|
70
|
+
this.data = Object.freeze(data);
|
|
103
71
|
}
|
|
104
72
|
|
|
105
73
|
#getTimestampExpired() {
|
package/src/utils/base62.js
CHANGED
package/src/utils/errors.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* @typedef {import('../token.js').Ecwt} Ecwt
|
|
4
|
+
*/
|
|
3
5
|
|
|
4
6
|
export class InvalidPackageInstanceError extends TypeError {
|
|
7
|
+
/**
|
|
8
|
+
* @param {string} property -
|
|
9
|
+
* @param {string} class_name -
|
|
10
|
+
* @param {string} package_name -
|
|
11
|
+
*/
|
|
5
12
|
constructor(property, class_name, package_name) {
|
|
6
13
|
super(`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.`);
|
|
7
14
|
}
|
|
@@ -20,8 +27,13 @@ export class EcwtParseError extends Error {
|
|
|
20
27
|
* Error thrown when parsed Ecwt is invalid.
|
|
21
28
|
*/
|
|
22
29
|
export class EcwtInvalidError extends Error {
|
|
30
|
+
message = 'Ecwt token is invalid.';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @param {Ecwt} ecwt -
|
|
34
|
+
*/
|
|
23
35
|
constructor(ecwt) {
|
|
24
|
-
super(
|
|
36
|
+
super();
|
|
25
37
|
|
|
26
38
|
this.ecwt = ecwt;
|
|
27
39
|
}
|
|
@@ -31,22 +43,12 @@ export class EcwtInvalidError extends Error {
|
|
|
31
43
|
* Error thrown when parsed Ecwt is expired.
|
|
32
44
|
*/
|
|
33
45
|
export class EcwtExpiredError extends EcwtInvalidError {
|
|
34
|
-
|
|
35
|
-
super();
|
|
36
|
-
|
|
37
|
-
this.ecwt = ecwt;
|
|
38
|
-
this.message = 'Ecwt is expired.';
|
|
39
|
-
}
|
|
46
|
+
message = 'Ecwt is expired.';
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/**
|
|
43
50
|
* Error thrown when parsed Ecwt is revoked.
|
|
44
51
|
*/
|
|
45
52
|
export class EcwtRevokedError extends EcwtInvalidError {
|
|
46
|
-
|
|
47
|
-
super();
|
|
48
|
-
|
|
49
|
-
this.ecwt = ecwt;
|
|
50
|
-
this.message = 'Ecwt is revoked.';
|
|
51
|
-
}
|
|
53
|
+
message = 'Ecwt is revoked.';
|
|
52
54
|
}
|
package/src/utils/time.js
CHANGED
package/tsconfig.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "esnext",
|
|
4
|
+
"module": "esnext",
|
|
5
|
+
"noImplicitAny": true,
|
|
6
|
+
"checkJs": true,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"allowSyntheticDefaultImports": true
|
|
10
|
+
},
|
|
11
|
+
"include": [
|
|
12
|
+
"src/**/*"
|
|
13
|
+
],
|
|
14
|
+
"exclude": [
|
|
15
|
+
"node_modules/",
|
|
16
|
+
"dist/",
|
|
17
|
+
"**/*.test.js"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
export class EcwtFactory {
|
|
2
|
+
/**
|
|
3
|
+
*
|
|
4
|
+
* @param {object} param0 -
|
|
5
|
+
* @param {import('redis').RedisClientType} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
6
|
+
* @param {LRUCache<string, CacheValue>} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
7
|
+
* @param {SnowflakeFactory} param0.snowflakeFactory SnowflakeFactory instance.
|
|
8
|
+
* @param {object} param0.options -
|
|
9
|
+
* @param {string} [param0.options.namespace] Namespace for Redis keys.
|
|
10
|
+
* @param {Buffer} param0.options.key Encryption key, 64 bytes
|
|
11
|
+
* @param {(value: any) => any} [param0.options.validator] Validator for token data. Should return validated value or throw an error.
|
|
12
|
+
* @param {Record<string, number>} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
13
|
+
*/
|
|
14
|
+
constructor({ redisClient, lruCache, snowflakeFactory, options: { namespace, key, validator, senml_key_map, }, }: {
|
|
15
|
+
redisClient?: import("redis").RedisClientType;
|
|
16
|
+
lruCache?: LRUCache<string, CacheValue>;
|
|
17
|
+
snowflakeFactory: SnowflakeFactory;
|
|
18
|
+
options: {
|
|
19
|
+
namespace?: string;
|
|
20
|
+
key: Buffer;
|
|
21
|
+
validator?: (value: any) => any;
|
|
22
|
+
senml_key_map?: Record<string, number>;
|
|
23
|
+
};
|
|
24
|
+
});
|
|
25
|
+
/**
|
|
26
|
+
* Creates new token.
|
|
27
|
+
* @async
|
|
28
|
+
* @param {object} data Data to be stored in token.
|
|
29
|
+
* @param {object} [options] -
|
|
30
|
+
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
31
|
+
* @returns {Promise<Ecwt>} -
|
|
32
|
+
*/
|
|
33
|
+
create(data: object, { ttl, }?: {
|
|
34
|
+
ttl?: number | null;
|
|
35
|
+
}): Promise<Ecwt>;
|
|
36
|
+
/**
|
|
37
|
+
* Parses token.
|
|
38
|
+
* @async
|
|
39
|
+
* @param {string} token String representation of token.
|
|
40
|
+
* @returns {Promise<Ecwt>} -
|
|
41
|
+
*/
|
|
42
|
+
verify(token: string): Promise<Ecwt>;
|
|
43
|
+
/**
|
|
44
|
+
* Parses token without throwing errors.
|
|
45
|
+
* @async
|
|
46
|
+
* @param {string} token String representation of token.
|
|
47
|
+
* @returns {Promise<{ success: true, ecwt: Ecwt } | { success: false, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
48
|
+
*/
|
|
49
|
+
safeVerify(token: string): Promise<{
|
|
50
|
+
success: true;
|
|
51
|
+
ecwt: Ecwt;
|
|
52
|
+
} | {
|
|
53
|
+
success: false;
|
|
54
|
+
ecwt: Ecwt | null;
|
|
55
|
+
}>;
|
|
56
|
+
/**
|
|
57
|
+
* Revokes token.
|
|
58
|
+
* @async
|
|
59
|
+
* @param {object} options -
|
|
60
|
+
* @param {string} options.token_id -
|
|
61
|
+
* @param {number} options.ts_ms_created -
|
|
62
|
+
* @param {number | null} options.ttl_initial -
|
|
63
|
+
* @returns {Promise<void>} -
|
|
64
|
+
*/
|
|
65
|
+
_revoke({ token_id, ts_ms_created, ttl_initial, }: {
|
|
66
|
+
token_id: string;
|
|
67
|
+
ts_ms_created: number;
|
|
68
|
+
ttl_initial: number | null;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Purges cache.
|
|
72
|
+
* @private
|
|
73
|
+
* @returns {void} -
|
|
74
|
+
*/
|
|
75
|
+
private _purgeCache;
|
|
76
|
+
#private;
|
|
77
|
+
}
|
|
78
|
+
export type Snowflake = import("@kirick/snowflake").Snowflake;
|
|
79
|
+
export type CacheValue = {
|
|
80
|
+
/**
|
|
81
|
+
* -
|
|
82
|
+
*/
|
|
83
|
+
snowflake: Snowflake;
|
|
84
|
+
/**
|
|
85
|
+
* -
|
|
86
|
+
*/
|
|
87
|
+
ttl_initial: number;
|
|
88
|
+
/**
|
|
89
|
+
* -
|
|
90
|
+
*/
|
|
91
|
+
data: Record<string, any>;
|
|
92
|
+
};
|
|
93
|
+
import { Ecwt } from './token.js';
|
|
94
|
+
import { LRUCache } from 'lru-cache';
|
|
95
|
+
import { SnowflakeFactory } from '@kirick/snowflake';
|
package/types/main.d.ts
ADDED
package/types/token.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('@kirick/snowflake').Snowflake} Snowflake
|
|
3
|
+
* @typedef {import('./factory.js').EcwtFactory} EcwtFactory
|
|
4
|
+
*/
|
|
5
|
+
export class Ecwt {
|
|
6
|
+
/**
|
|
7
|
+
* @param {EcwtFactory} ecwtFactory -
|
|
8
|
+
* @param {object} options -
|
|
9
|
+
* @param {string} options.token String representation of token.
|
|
10
|
+
* @param {Snowflake} options.snowflake -
|
|
11
|
+
* @param {number?} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
12
|
+
* @param {object} options.data Data stored in token.
|
|
13
|
+
*/
|
|
14
|
+
constructor(ecwtFactory: EcwtFactory, { token, snowflake, ttl_initial, data, }: {
|
|
15
|
+
token: string;
|
|
16
|
+
snowflake: Snowflake;
|
|
17
|
+
ttl_initial: number | null;
|
|
18
|
+
data: object;
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Token string representation.
|
|
22
|
+
* @type {string}
|
|
23
|
+
* @readonly
|
|
24
|
+
*/
|
|
25
|
+
readonly token: string;
|
|
26
|
+
/**
|
|
27
|
+
* Token ID.
|
|
28
|
+
* @type {string}
|
|
29
|
+
* @readonly
|
|
30
|
+
*/
|
|
31
|
+
readonly id: string;
|
|
32
|
+
/**
|
|
33
|
+
* Snowflake associated with token.
|
|
34
|
+
* @type {Snowflake}
|
|
35
|
+
* @readonly
|
|
36
|
+
*/
|
|
37
|
+
readonly snowflake: Snowflake;
|
|
38
|
+
/**
|
|
39
|
+
* Timestamp when token expires in seconds.
|
|
40
|
+
* @type {number?}
|
|
41
|
+
* @readonly
|
|
42
|
+
*/
|
|
43
|
+
readonly ts_expired: number | null;
|
|
44
|
+
/**
|
|
45
|
+
* Data stored in token.
|
|
46
|
+
* @type {{ [key: string]: any }}
|
|
47
|
+
* @readonly
|
|
48
|
+
*/
|
|
49
|
+
readonly data: {
|
|
50
|
+
[key: string]: any;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Actual time to live in seconds.
|
|
54
|
+
* @returns {number | null} -
|
|
55
|
+
*/
|
|
56
|
+
getTTL(): number | null;
|
|
57
|
+
/**
|
|
58
|
+
* Revokes token.
|
|
59
|
+
* @returns {Promise<void>} -
|
|
60
|
+
*/
|
|
61
|
+
revoke(): Promise<void>;
|
|
62
|
+
#private;
|
|
63
|
+
}
|
|
64
|
+
export type Snowflake = import("@kirick/snowflake").Snowflake;
|
|
65
|
+
export type EcwtFactory = import("./factory.js").EcwtFactory;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('../token.js').Ecwt} Ecwt
|
|
3
|
+
*/
|
|
4
|
+
export class InvalidPackageInstanceError extends TypeError {
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} property -
|
|
7
|
+
* @param {string} class_name -
|
|
8
|
+
* @param {string} package_name -
|
|
9
|
+
*/
|
|
10
|
+
constructor(property: string, class_name: string, package_name: string);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Error thrown when string token cannot be parsed to Ecwt.
|
|
14
|
+
*/
|
|
15
|
+
export class EcwtParseError extends Error {
|
|
16
|
+
constructor();
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown when parsed Ecwt is invalid.
|
|
20
|
+
*/
|
|
21
|
+
export class EcwtInvalidError extends Error {
|
|
22
|
+
/**
|
|
23
|
+
* @param {Ecwt} ecwt -
|
|
24
|
+
*/
|
|
25
|
+
constructor(ecwt: Ecwt);
|
|
26
|
+
ecwt: import("../token.js").Ecwt;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Error thrown when parsed Ecwt is expired.
|
|
30
|
+
*/
|
|
31
|
+
export class EcwtExpiredError extends EcwtInvalidError {
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when parsed Ecwt is revoked.
|
|
35
|
+
*/
|
|
36
|
+
export class EcwtRevokedError extends EcwtInvalidError {
|
|
37
|
+
}
|
|
38
|
+
export type Ecwt = import("../token.js").Ecwt;
|