ecwt 0.2.0-beta.5 → 0.2.1-beta.2
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} +41 -61
- package/package.json +19 -17
- package/src/factory.js +35 -19
- package/src/main.js +0 -2
- package/src/token.js +14 -43
- 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 +97 -0
- package/types/factory.test.d.ts +1 -0
- package/types/main.d.ts +3 -0
- package/types/token.d.ts +66 -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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -51,21 +52,7 @@ function toSeconds(value) {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
// 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
55
|
var Ecwt = class {
|
|
67
|
-
#ecwtFactory;
|
|
68
|
-
#ttl_initial;
|
|
69
56
|
/**
|
|
70
57
|
* Token string representation.
|
|
71
58
|
* @type {string}
|
|
@@ -92,17 +79,21 @@ var Ecwt = class {
|
|
|
92
79
|
ts_expired;
|
|
93
80
|
/**
|
|
94
81
|
* Data stored in token.
|
|
95
|
-
* @type {
|
|
82
|
+
* @type {D}
|
|
96
83
|
* @readonly
|
|
97
84
|
*/
|
|
98
85
|
data;
|
|
86
|
+
/** @type {EcwtFactory} */
|
|
87
|
+
#ecwtFactory;
|
|
88
|
+
/** @type {number | null} */
|
|
89
|
+
#ttl_initial;
|
|
99
90
|
/**
|
|
100
91
|
* @param {EcwtFactory} ecwtFactory -
|
|
101
92
|
* @param {object} options -
|
|
102
93
|
* @param {string} options.token String representation of token.
|
|
103
94
|
* @param {Snowflake} options.snowflake -
|
|
104
|
-
* @param {number
|
|
105
|
-
* @param {
|
|
95
|
+
* @param {number | null} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
96
|
+
* @param {D} options.data Data stored in token.
|
|
106
97
|
*/
|
|
107
98
|
constructor(ecwtFactory, {
|
|
108
99
|
token,
|
|
@@ -112,23 +103,11 @@ var Ecwt = class {
|
|
|
112
103
|
}) {
|
|
113
104
|
this.#ecwtFactory = ecwtFactory;
|
|
114
105
|
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
|
-
);
|
|
106
|
+
this.token = token;
|
|
107
|
+
this.id = snowflake.toBase62();
|
|
108
|
+
this.snowflake = snowflake;
|
|
109
|
+
this.ts_expired = this.#getTimestampExpired();
|
|
110
|
+
this.data = Object.freeze(data);
|
|
132
111
|
}
|
|
133
112
|
#getTimestampExpired() {
|
|
134
113
|
if (this.#ttl_initial === null) {
|
|
@@ -166,6 +145,11 @@ var base62 = (0, import_base_x.default)("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabc
|
|
|
166
145
|
|
|
167
146
|
// src/utils/errors.js
|
|
168
147
|
var InvalidPackageInstanceError = class extends TypeError {
|
|
148
|
+
/**
|
|
149
|
+
* @param {string} property -
|
|
150
|
+
* @param {string} class_name -
|
|
151
|
+
* @param {string} package_name -
|
|
152
|
+
*/
|
|
169
153
|
constructor(property, class_name, package_name) {
|
|
170
154
|
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
155
|
}
|
|
@@ -176,24 +160,20 @@ var EcwtParseError = class extends Error {
|
|
|
176
160
|
}
|
|
177
161
|
};
|
|
178
162
|
var EcwtInvalidError = class extends Error {
|
|
163
|
+
message = "Ecwt token is invalid.";
|
|
164
|
+
/**
|
|
165
|
+
* @param {Ecwt} ecwt -
|
|
166
|
+
*/
|
|
179
167
|
constructor(ecwt) {
|
|
180
|
-
super(
|
|
168
|
+
super();
|
|
181
169
|
this.ecwt = ecwt;
|
|
182
170
|
}
|
|
183
171
|
};
|
|
184
172
|
var EcwtExpiredError = class extends EcwtInvalidError {
|
|
185
|
-
|
|
186
|
-
super();
|
|
187
|
-
this.ecwt = ecwt;
|
|
188
|
-
this.message = "Ecwt is expired.";
|
|
189
|
-
}
|
|
173
|
+
message = "Ecwt is expired.";
|
|
190
174
|
};
|
|
191
175
|
var EcwtRevokedError = class extends EcwtInvalidError {
|
|
192
|
-
|
|
193
|
-
super();
|
|
194
|
-
this.ecwt = ecwt;
|
|
195
|
-
this.message = "Ecwt is revoked.";
|
|
196
|
-
}
|
|
176
|
+
message = "Ecwt is revoked.";
|
|
197
177
|
};
|
|
198
178
|
|
|
199
179
|
// src/factory.js
|
|
@@ -212,21 +192,21 @@ var EcwtFactory = class {
|
|
|
212
192
|
#redisClient;
|
|
213
193
|
#lruCache;
|
|
214
194
|
#snowflakeFactory;
|
|
215
|
-
#
|
|
195
|
+
#redis_key_revoked;
|
|
216
196
|
#encryption_key;
|
|
217
197
|
#validator;
|
|
218
|
-
|
|
198
|
+
/** @type {CborEncoder | null} */
|
|
199
|
+
#cborEncoder = null;
|
|
219
200
|
/**
|
|
220
|
-
*
|
|
221
201
|
* @param {object} param0 -
|
|
222
|
-
* @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.
|
|
202
|
+
* @param {import('redis').RedisClientType<import('redis').RedisModules, import('redis').RedisFunctions, import('redis').RedisScripts>} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
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) {
|
|
@@ -275,7 +255,7 @@ var EcwtFactory = class {
|
|
|
275
255
|
/**
|
|
276
256
|
* Creates new token.
|
|
277
257
|
* @async
|
|
278
|
-
* @param {
|
|
258
|
+
* @param {D} data Data to be stored in token.
|
|
279
259
|
* @param {object} [options] -
|
|
280
260
|
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
281
261
|
* @returns {Promise<Ecwt>} -
|
|
@@ -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
|
-
|
|
331
|
-
{
|
|
332
|
-
ttl:
|
|
310
|
+
cache_value,
|
|
311
|
+
cache_value.ttl_initial === null ? void 0 : {
|
|
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.2",
|
|
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 | null} 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
|
|
@@ -36,28 +47,31 @@ const redisClient = createClient();
|
|
|
36
47
|
const redis_client_constructor_name = redisClient.constructor.name;
|
|
37
48
|
const redis_client_keys = getAllKeysList(redisClient);
|
|
38
49
|
|
|
50
|
+
/**
|
|
51
|
+
* @template {Record<string, any>} [D=Record<string, any>]
|
|
52
|
+
*/
|
|
39
53
|
export class EcwtFactory {
|
|
40
54
|
#redisClient;
|
|
41
55
|
#lruCache;
|
|
42
56
|
#snowflakeFactory;
|
|
43
57
|
|
|
44
|
-
#
|
|
58
|
+
#redis_key_revoked;
|
|
45
59
|
#encryption_key;
|
|
46
60
|
|
|
47
61
|
#validator;
|
|
48
|
-
|
|
62
|
+
/** @type {CborEncoder | null} */
|
|
63
|
+
#cborEncoder = null;
|
|
49
64
|
|
|
50
65
|
/**
|
|
51
|
-
*
|
|
52
66
|
* @param {object} param0 -
|
|
53
|
-
* @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.
|
|
67
|
+
* @param {import('redis').RedisClientType<import('redis').RedisModules, import('redis').RedisFunctions, import('redis').RedisScripts>} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
68
|
+
* @param {LRUCache<string, CacheValue>} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
55
69
|
* @param {SnowflakeFactory} param0.snowflakeFactory SnowflakeFactory instance.
|
|
56
70
|
* @param {object} param0.options -
|
|
57
71
|
* @param {string} [param0.options.namespace] Namespace for Redis keys.
|
|
58
72
|
* @param {Buffer} param0.options.key Encryption key, 64 bytes
|
|
59
73
|
* @param {(value: any) => any} [param0.options.validator] Validator for token data. Should return validated value or throw an error.
|
|
60
|
-
* @param {
|
|
74
|
+
* @param {Record<string, number>} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
61
75
|
*/
|
|
62
76
|
constructor({
|
|
63
77
|
redisClient,
|
|
@@ -106,7 +120,7 @@ export class EcwtFactory {
|
|
|
106
120
|
}
|
|
107
121
|
this.#snowflakeFactory = snowflakeFactory;
|
|
108
122
|
|
|
109
|
-
this.#
|
|
123
|
+
this.#redis_key_revoked = `${REDIS_PREFIX}${namespace}:revoked`;
|
|
110
124
|
|
|
111
125
|
this.#encryption_key = key;
|
|
112
126
|
|
|
@@ -122,7 +136,7 @@ export class EcwtFactory {
|
|
|
122
136
|
/**
|
|
123
137
|
* Creates new token.
|
|
124
138
|
* @async
|
|
125
|
-
* @param {
|
|
139
|
+
* @param {D} data Data to be stored in token.
|
|
126
140
|
* @param {object} [options] -
|
|
127
141
|
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
128
142
|
* @returns {Promise<Ecwt>} -
|
|
@@ -186,15 +200,17 @@ export class EcwtFactory {
|
|
|
186
200
|
/**
|
|
187
201
|
* Sets data to cache.
|
|
188
202
|
* @param {string} token String representation of token.
|
|
189
|
-
* @param {
|
|
203
|
+
* @param {CacheValue} cache_value Data to be stored in cache.
|
|
190
204
|
*/
|
|
191
|
-
#setCache(token,
|
|
205
|
+
#setCache(token, cache_value) {
|
|
192
206
|
this.#lruCache?.set(
|
|
193
207
|
token,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
208
|
+
cache_value,
|
|
209
|
+
cache_value.ttl_initial === null
|
|
210
|
+
? undefined
|
|
211
|
+
: {
|
|
212
|
+
ttl: cache_value.ttl_initial * 1000,
|
|
213
|
+
},
|
|
198
214
|
);
|
|
199
215
|
}
|
|
200
216
|
|
|
@@ -296,7 +312,7 @@ export class EcwtFactory {
|
|
|
296
312
|
|
|
297
313
|
if (this.#redisClient) {
|
|
298
314
|
const score = await this.#redisClient.ZSCORE(
|
|
299
|
-
this.#
|
|
315
|
+
this.#redis_key_revoked,
|
|
300
316
|
ecwt.id,
|
|
301
317
|
);
|
|
302
318
|
if (score !== null) {
|
|
@@ -370,13 +386,13 @@ export class EcwtFactory {
|
|
|
370
386
|
await this.#redisClient.MULTI()
|
|
371
387
|
.addCommand([
|
|
372
388
|
'ZADD',
|
|
373
|
-
this.#
|
|
389
|
+
this.#redis_key_revoked,
|
|
374
390
|
String(ts_ms_expired),
|
|
375
391
|
token_id,
|
|
376
392
|
])
|
|
377
393
|
.addCommand([
|
|
378
394
|
'ZREMRANGEBYSCORE',
|
|
379
|
-
this.#
|
|
395
|
+
this.#redis_key_revoked,
|
|
380
396
|
'-inf',
|
|
381
397
|
String(Date.now()),
|
|
382
398
|
])
|
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
|
/**
|
|
@@ -9,28 +7,9 @@ import { toSeconds } from './utils/time.js';
|
|
|
9
7
|
*/
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param {object} target -
|
|
14
|
-
* @param {string} key -
|
|
15
|
-
* @param {any} value -
|
|
10
|
+
* @template {Record<string, any>} [D=Record<string, any>]
|
|
16
11
|
*/
|
|
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
12
|
export class Ecwt {
|
|
31
|
-
#ecwtFactory;
|
|
32
|
-
#ttl_initial;
|
|
33
|
-
|
|
34
13
|
/**
|
|
35
14
|
* Token string representation.
|
|
36
15
|
* @type {string}
|
|
@@ -57,18 +36,23 @@ export class Ecwt {
|
|
|
57
36
|
ts_expired;
|
|
58
37
|
/**
|
|
59
38
|
* Data stored in token.
|
|
60
|
-
* @type {
|
|
39
|
+
* @type {D}
|
|
61
40
|
* @readonly
|
|
62
41
|
*/
|
|
63
42
|
data;
|
|
64
43
|
|
|
44
|
+
/** @type {EcwtFactory} */
|
|
45
|
+
#ecwtFactory;
|
|
46
|
+
/** @type {number | null} */
|
|
47
|
+
#ttl_initial;
|
|
48
|
+
|
|
65
49
|
/**
|
|
66
50
|
* @param {EcwtFactory} ecwtFactory -
|
|
67
51
|
* @param {object} options -
|
|
68
52
|
* @param {string} options.token String representation of token.
|
|
69
53
|
* @param {Snowflake} options.snowflake -
|
|
70
|
-
* @param {number
|
|
71
|
-
* @param {
|
|
54
|
+
* @param {number | null} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
55
|
+
* @param {D} options.data Data stored in token.
|
|
72
56
|
*/
|
|
73
57
|
constructor(
|
|
74
58
|
ecwtFactory,
|
|
@@ -80,26 +64,13 @@ export class Ecwt {
|
|
|
80
64
|
},
|
|
81
65
|
) {
|
|
82
66
|
this.#ecwtFactory = ecwtFactory;
|
|
83
|
-
|
|
84
67
|
this.#ttl_initial = ttl_initial;
|
|
85
68
|
|
|
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
|
-
);
|
|
69
|
+
this.token = token;
|
|
70
|
+
this.id = snowflake.toBase62();
|
|
71
|
+
this.snowflake = snowflake;
|
|
72
|
+
this.ts_expired = this.#getTimestampExpired();
|
|
73
|
+
this.data = Object.freeze(data);
|
|
103
74
|
}
|
|
104
75
|
|
|
105
76
|
#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
|
+
"outDir": "./dist/",
|
|
4
|
+
"target": "esnext",
|
|
5
|
+
"module": "esnext",
|
|
6
|
+
"checkJs": true,
|
|
7
|
+
"allowJs": true,
|
|
8
|
+
"moduleResolution": "node",
|
|
9
|
+
"allowSyntheticDefaultImports": true,
|
|
10
|
+
"strict": true
|
|
11
|
+
},
|
|
12
|
+
"include": [
|
|
13
|
+
"src/**/*"
|
|
14
|
+
],
|
|
15
|
+
"exclude": [
|
|
16
|
+
"node_modules/",
|
|
17
|
+
"dist/"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template {Record<string, any>} [D=Record<string, any>]
|
|
3
|
+
*/
|
|
4
|
+
export class EcwtFactory<D extends Record<string, any> = Record<string, any>> {
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} param0 -
|
|
7
|
+
* @param {import('redis').RedisClientType<import('redis').RedisModules, import('redis').RedisFunctions, import('redis').RedisScripts>} [param0.redisClient] RedisClient instance. If not provided, tokens will not be revoked and cannot be checked for revocation.
|
|
8
|
+
* @param {LRUCache<string, CacheValue>} [param0.lruCache] LRUCache instance. If not provided, tokens will be decrypted every time they are verified.
|
|
9
|
+
* @param {SnowflakeFactory} param0.snowflakeFactory SnowflakeFactory instance.
|
|
10
|
+
* @param {object} param0.options -
|
|
11
|
+
* @param {string} [param0.options.namespace] Namespace for Redis keys.
|
|
12
|
+
* @param {Buffer} param0.options.key Encryption key, 64 bytes
|
|
13
|
+
* @param {(value: any) => any} [param0.options.validator] Validator for token data. Should return validated value or throw an error.
|
|
14
|
+
* @param {Record<string, number>} [param0.options.senml_key_map] Payload object keys mapped for their SenML keys.
|
|
15
|
+
*/
|
|
16
|
+
constructor({ redisClient, lruCache, snowflakeFactory, options: { namespace, key, validator, senml_key_map, }, }: {
|
|
17
|
+
redisClient?: import("redis").RedisClientType<import("redis").RedisModules, import("redis").RedisFunctions, import("redis").RedisScripts> | undefined;
|
|
18
|
+
lruCache?: LRUCache<string, CacheValue, any> | undefined;
|
|
19
|
+
snowflakeFactory: SnowflakeFactory;
|
|
20
|
+
options: {
|
|
21
|
+
namespace?: string | undefined;
|
|
22
|
+
key: Buffer;
|
|
23
|
+
validator?: ((value: any) => any) | undefined;
|
|
24
|
+
senml_key_map?: Record<string, number> | undefined;
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Creates new token.
|
|
29
|
+
* @async
|
|
30
|
+
* @param {D} data Data to be stored in token.
|
|
31
|
+
* @param {object} [options] -
|
|
32
|
+
* @param {number | null} [options.ttl] Time to live in seconds. By default, token will never expire.
|
|
33
|
+
* @returns {Promise<Ecwt>} -
|
|
34
|
+
*/
|
|
35
|
+
create(data: D, { ttl, }?: {
|
|
36
|
+
ttl?: number | null | undefined;
|
|
37
|
+
} | undefined): Promise<Ecwt>;
|
|
38
|
+
/**
|
|
39
|
+
* Parses token.
|
|
40
|
+
* @async
|
|
41
|
+
* @param {string} token String representation of token.
|
|
42
|
+
* @returns {Promise<Ecwt>} -
|
|
43
|
+
*/
|
|
44
|
+
verify(token: string): Promise<Ecwt>;
|
|
45
|
+
/**
|
|
46
|
+
* Parses token without throwing errors.
|
|
47
|
+
* @async
|
|
48
|
+
* @param {string} token String representation of token.
|
|
49
|
+
* @returns {Promise<{ success: true, ecwt: Ecwt } | { success: false, ecwt: Ecwt | null }>} Returns whether token was parsed and verified successfully and Ecwt if parsed.
|
|
50
|
+
*/
|
|
51
|
+
safeVerify(token: string): Promise<{
|
|
52
|
+
success: true;
|
|
53
|
+
ecwt: Ecwt;
|
|
54
|
+
} | {
|
|
55
|
+
success: false;
|
|
56
|
+
ecwt: Ecwt | null;
|
|
57
|
+
}>;
|
|
58
|
+
/**
|
|
59
|
+
* Revokes token.
|
|
60
|
+
* @async
|
|
61
|
+
* @param {object} options -
|
|
62
|
+
* @param {string} options.token_id -
|
|
63
|
+
* @param {number} options.ts_ms_created -
|
|
64
|
+
* @param {number | null} options.ttl_initial -
|
|
65
|
+
* @returns {Promise<void>} -
|
|
66
|
+
*/
|
|
67
|
+
_revoke({ token_id, ts_ms_created, ttl_initial, }: {
|
|
68
|
+
token_id: string;
|
|
69
|
+
ts_ms_created: number;
|
|
70
|
+
ttl_initial: number | null;
|
|
71
|
+
}): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Purges cache.
|
|
74
|
+
* @private
|
|
75
|
+
* @returns {void} -
|
|
76
|
+
*/
|
|
77
|
+
private _purgeCache;
|
|
78
|
+
#private;
|
|
79
|
+
}
|
|
80
|
+
export type Snowflake = import("@kirick/snowflake").Snowflake;
|
|
81
|
+
export type CacheValue = {
|
|
82
|
+
/**
|
|
83
|
+
* -
|
|
84
|
+
*/
|
|
85
|
+
snowflake: Snowflake;
|
|
86
|
+
/**
|
|
87
|
+
* -
|
|
88
|
+
*/
|
|
89
|
+
ttl_initial: number | null;
|
|
90
|
+
/**
|
|
91
|
+
* -
|
|
92
|
+
*/
|
|
93
|
+
data: Record<string, any>;
|
|
94
|
+
};
|
|
95
|
+
import { Ecwt } from './token.js';
|
|
96
|
+
import { LRUCache } from 'lru-cache';
|
|
97
|
+
import { SnowflakeFactory } from '@kirick/snowflake';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/types/main.d.ts
ADDED
package/types/token.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('@kirick/snowflake').Snowflake} Snowflake
|
|
3
|
+
* @typedef {import('./factory.js').EcwtFactory} EcwtFactory
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @template {Record<string, any>} [D=Record<string, any>]
|
|
7
|
+
*/
|
|
8
|
+
export class Ecwt<D extends Record<string, any> = Record<string, any>> {
|
|
9
|
+
/**
|
|
10
|
+
* @param {EcwtFactory} ecwtFactory -
|
|
11
|
+
* @param {object} options -
|
|
12
|
+
* @param {string} options.token String representation of token.
|
|
13
|
+
* @param {Snowflake} options.snowflake -
|
|
14
|
+
* @param {number | null} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
15
|
+
* @param {D} options.data Data stored in token.
|
|
16
|
+
*/
|
|
17
|
+
constructor(ecwtFactory: EcwtFactory, { token, snowflake, ttl_initial, data, }: {
|
|
18
|
+
token: string;
|
|
19
|
+
snowflake: Snowflake;
|
|
20
|
+
ttl_initial: number | null;
|
|
21
|
+
data: D;
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Token string representation.
|
|
25
|
+
* @type {string}
|
|
26
|
+
* @readonly
|
|
27
|
+
*/
|
|
28
|
+
readonly token: string;
|
|
29
|
+
/**
|
|
30
|
+
* Token ID.
|
|
31
|
+
* @type {string}
|
|
32
|
+
* @readonly
|
|
33
|
+
*/
|
|
34
|
+
readonly id: string;
|
|
35
|
+
/**
|
|
36
|
+
* Snowflake associated with token.
|
|
37
|
+
* @type {Snowflake}
|
|
38
|
+
* @readonly
|
|
39
|
+
*/
|
|
40
|
+
readonly snowflake: Snowflake;
|
|
41
|
+
/**
|
|
42
|
+
* Timestamp when token expires in seconds.
|
|
43
|
+
* @type {number?}
|
|
44
|
+
* @readonly
|
|
45
|
+
*/
|
|
46
|
+
readonly ts_expired: number | null;
|
|
47
|
+
/**
|
|
48
|
+
* Data stored in token.
|
|
49
|
+
* @type {D}
|
|
50
|
+
* @readonly
|
|
51
|
+
*/
|
|
52
|
+
readonly data: D;
|
|
53
|
+
/**
|
|
54
|
+
* Actual time to live in seconds.
|
|
55
|
+
* @returns {number | null} -
|
|
56
|
+
*/
|
|
57
|
+
getTTL(): number | null;
|
|
58
|
+
/**
|
|
59
|
+
* Revokes token.
|
|
60
|
+
* @returns {Promise<void>} -
|
|
61
|
+
*/
|
|
62
|
+
revoke(): Promise<void>;
|
|
63
|
+
#private;
|
|
64
|
+
}
|
|
65
|
+
export type Snowflake = import("@kirick/snowflake").Snowflake;
|
|
66
|
+
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<Record<string, any>>;
|
|
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;
|