ecwt 0.1.0-beta.3 → 0.1.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/README.md +24 -15
- package/dist/ecwt.cjs +81 -34
- package/package.json +2 -2
- package/src/factory.js +7 -7
- package/src/main.js +5 -0
- package/src/token.js +78 -34
package/README.md
CHANGED
|
@@ -147,19 +147,24 @@ create(
|
|
|
147
147
|
[key: string]: any,
|
|
148
148
|
},
|
|
149
149
|
options: {
|
|
150
|
-
ttl: number
|
|
150
|
+
ttl: number | null,
|
|
151
151
|
}
|
|
152
152
|
): Promise<Ecwt>
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
Creates a token.
|
|
156
156
|
|
|
157
|
-
`options.ttl` specifies the time to live of the token in seconds. If
|
|
157
|
+
`options.ttl` specifies the time to live of the token in seconds. If set to null, token will never expire.
|
|
158
|
+
|
|
159
|
+
> **Be careful with `ttl = null`!**
|
|
160
|
+
>
|
|
161
|
+
> Revoked tokens are stored in Redis until they expire. Never-expiring tokens will be stored in Redis **forever**, which will lead to uncontrolled Redis database growth.
|
|
158
162
|
|
|
159
163
|
Returns `Ecwt` instance.
|
|
160
164
|
|
|
161
165
|
```javascript
|
|
162
|
-
|
|
166
|
+
// Example
|
|
167
|
+
const ecwt = await ecwtFactory.create(
|
|
163
168
|
{
|
|
164
169
|
user_id: 1,
|
|
165
170
|
nick: 'kirick',
|
|
@@ -190,40 +195,44 @@ Returns `Ecwt` instance.
|
|
|
190
195
|
If the token is invalid, throws `EcwtInvalidError` which contains `Ecwt` instance in the `ecwt` property.
|
|
191
196
|
|
|
192
197
|
```javascript
|
|
193
|
-
const
|
|
198
|
+
const ecwt = await ecwtFactory.verify(token);
|
|
194
199
|
```
|
|
195
200
|
|
|
196
201
|
### `Ecwt`
|
|
197
202
|
|
|
198
|
-
Represents the token.
|
|
203
|
+
Represents the token. Its counstructor cannot be called by the user.
|
|
199
204
|
|
|
200
205
|
```javascript
|
|
201
206
|
import { Ecwt } from 'ecwt';
|
|
202
207
|
```
|
|
203
208
|
|
|
204
|
-
#### Class property `token: string`
|
|
209
|
+
#### Class property `readonly token: string`
|
|
205
210
|
|
|
206
211
|
The string representation of the token.
|
|
207
212
|
|
|
208
|
-
#### Class property `id: string`
|
|
213
|
+
#### Class property `readonly id: string`
|
|
209
214
|
|
|
210
215
|
The unique ID of the token.
|
|
211
216
|
|
|
212
|
-
#### Class property `snowflake: Snowflake`
|
|
217
|
+
#### Class property `readonly snowflake: Snowflake`
|
|
213
218
|
|
|
214
219
|
The `Snowflake` instance of the token. For documentation, see [snowflake-js repository](https://github.com/kirick13/snowflake-js).
|
|
215
220
|
|
|
216
|
-
#### Class property `ts_expired: number`
|
|
221
|
+
#### Class property `readonly ts_expired: number | null`
|
|
217
222
|
|
|
218
|
-
The timestamp of the token expiration in seconds.
|
|
223
|
+
The timestamp of the token expiration in seconds. Equals to `null` if the token does not expire.
|
|
219
224
|
|
|
220
|
-
#### Class property `
|
|
225
|
+
#### Class property `readonly data: { [key: string]: any }`
|
|
221
226
|
|
|
222
|
-
|
|
227
|
+
The payload of the token.
|
|
223
228
|
|
|
224
|
-
#### Class
|
|
229
|
+
#### Class method `getTTL`
|
|
225
230
|
|
|
226
|
-
|
|
231
|
+
```typescript
|
|
232
|
+
getTTL(): number | null
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Returns current the time to live of the token in seconds. If the token does not expire, returns `null`.
|
|
227
236
|
|
|
228
237
|
#### Class method `revoke`
|
|
229
238
|
|
|
@@ -231,4 +240,4 @@ The payload of the token.
|
|
|
231
240
|
revoke(): Promise<void>
|
|
232
241
|
```
|
|
233
242
|
|
|
234
|
-
Revokes the token.
|
|
243
|
+
Revokes the token. Attempts to verify the revoked token will throw `EcwtRevokedError`.
|
package/dist/ecwt.cjs
CHANGED
|
@@ -30,7 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
var main_exports = {};
|
|
31
31
|
__export(main_exports, {
|
|
32
32
|
Ecwt: () => Ecwt,
|
|
33
|
-
|
|
33
|
+
EcwtExpiredError: () => EcwtExpiredError,
|
|
34
|
+
EcwtFactory: () => EcwtFactory,
|
|
35
|
+
EcwtInvalidError: () => EcwtInvalidError,
|
|
36
|
+
EcwtRevokedError: () => EcwtRevokedError
|
|
34
37
|
});
|
|
35
38
|
module.exports = __toCommonJS(main_exports);
|
|
36
39
|
|
|
@@ -47,18 +50,57 @@ function toSeconds(value) {
|
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
// src/token.js
|
|
53
|
+
function assign(target, key, value) {
|
|
54
|
+
Object.defineProperty(
|
|
55
|
+
target,
|
|
56
|
+
key,
|
|
57
|
+
{
|
|
58
|
+
value,
|
|
59
|
+
enumerable: true,
|
|
60
|
+
writable: false,
|
|
61
|
+
configurable: false
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
}
|
|
50
65
|
var Ecwt = class {
|
|
51
66
|
#ecwtFactory;
|
|
52
|
-
#token;
|
|
53
|
-
#snowflake;
|
|
54
67
|
#ttl_initial;
|
|
55
|
-
|
|
68
|
+
/**
|
|
69
|
+
* Token string representation.
|
|
70
|
+
* @type {string}
|
|
71
|
+
* @readonly
|
|
72
|
+
*/
|
|
73
|
+
token;
|
|
74
|
+
/**
|
|
75
|
+
* Token ID.
|
|
76
|
+
* @type {string}
|
|
77
|
+
* @readonly
|
|
78
|
+
*/
|
|
79
|
+
id;
|
|
80
|
+
/**
|
|
81
|
+
* Snowflake associated with token.
|
|
82
|
+
* @type {Snowflake}
|
|
83
|
+
* @readonly
|
|
84
|
+
*/
|
|
85
|
+
snowflake;
|
|
86
|
+
/**
|
|
87
|
+
* Timestamp when token expires in seconds.
|
|
88
|
+
* @type {number?}
|
|
89
|
+
* @readonly
|
|
90
|
+
*/
|
|
91
|
+
ts_expired;
|
|
92
|
+
/**
|
|
93
|
+
* Data stored in token.
|
|
94
|
+
* @type {{ [key: string]: any }}
|
|
95
|
+
* @readonly
|
|
96
|
+
*/
|
|
97
|
+
data;
|
|
56
98
|
/**
|
|
57
99
|
* @param {EcwtFactory} ecwtFactory -
|
|
58
100
|
* @param {object} options -
|
|
59
101
|
* @param {string} options.token String representation of token.
|
|
60
102
|
* @param {Snowflake} options.snowflake -
|
|
61
|
-
* @param {number
|
|
103
|
+
* @param {number?} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
62
104
|
* @param {object} options.data Data stored in token.
|
|
63
105
|
*/
|
|
64
106
|
constructor(ecwtFactory, {
|
|
@@ -68,45 +110,46 @@ var Ecwt = class {
|
|
|
68
110
|
data
|
|
69
111
|
}) {
|
|
70
112
|
this.#ecwtFactory = ecwtFactory;
|
|
71
|
-
this.#token = token;
|
|
72
|
-
this.#snowflake = snowflake;
|
|
73
113
|
this.#ttl_initial = ttl_initial;
|
|
74
|
-
this
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
assign(this, "token", token);
|
|
115
|
+
assign(
|
|
116
|
+
this,
|
|
117
|
+
"id",
|
|
118
|
+
snowflake.toBase62()
|
|
119
|
+
);
|
|
120
|
+
assign(this, "snowflake", snowflake);
|
|
121
|
+
assign(
|
|
122
|
+
this,
|
|
123
|
+
"ts_expired",
|
|
124
|
+
this.#getTimestampExpired()
|
|
125
|
+
);
|
|
126
|
+
assign(
|
|
127
|
+
this,
|
|
128
|
+
"data",
|
|
129
|
+
Object.freeze(data)
|
|
130
|
+
);
|
|
84
131
|
}
|
|
85
|
-
|
|
132
|
+
#getTimestampExpired() {
|
|
86
133
|
if (this.#ttl_initial === null) {
|
|
87
|
-
return
|
|
134
|
+
return null;
|
|
88
135
|
}
|
|
89
|
-
return toSeconds(this
|
|
136
|
+
return toSeconds(this.snowflake.timestamp) + this.#ttl_initial;
|
|
90
137
|
}
|
|
91
138
|
/**
|
|
92
|
-
*
|
|
93
|
-
* @
|
|
94
|
-
* @readonly
|
|
139
|
+
* Actual time to live in seconds.
|
|
140
|
+
* @returns {number | null} -
|
|
95
141
|
*/
|
|
96
|
-
|
|
142
|
+
getTTL() {
|
|
97
143
|
if (this.#ttl_initial === null) {
|
|
98
|
-
return
|
|
144
|
+
return null;
|
|
99
145
|
}
|
|
100
|
-
return this.#ttl_initial - toSeconds(Date.now() - this
|
|
101
|
-
}
|
|
102
|
-
get data() {
|
|
103
|
-
return this.#data;
|
|
146
|
+
return this.#ttl_initial - toSeconds(Date.now() - this.snowflake.timestamp);
|
|
104
147
|
}
|
|
105
148
|
/* async */
|
|
106
149
|
revoke() {
|
|
107
150
|
return this.#ecwtFactory._revoke({
|
|
108
151
|
token_id: this.id,
|
|
109
|
-
ts_ms_created: this
|
|
152
|
+
ts_ms_created: this.snowflake.timestamp,
|
|
110
153
|
ttl_initial: this.#ttl_initial
|
|
111
154
|
});
|
|
112
155
|
}
|
|
@@ -233,12 +276,12 @@ var EcwtFactory = class {
|
|
|
233
276
|
}
|
|
234
277
|
payload.push(value);
|
|
235
278
|
}
|
|
236
|
-
if (typeof ttl !== "number" && Number.isNaN(ttl) !== true
|
|
237
|
-
|
|
279
|
+
if (typeof ttl !== "number" && Number.isNaN(ttl) !== true && ttl !== null) {
|
|
280
|
+
throw new TypeError("TTL must be a number or null.");
|
|
238
281
|
}
|
|
239
282
|
const snowflake = await this.#snowflakeFactory.createSafe();
|
|
240
283
|
const token_raw = (0, import_cbor_x.encode)([
|
|
241
|
-
snowflake.
|
|
284
|
+
snowflake.toBuffer(),
|
|
242
285
|
ttl,
|
|
243
286
|
payload
|
|
244
287
|
]);
|
|
@@ -360,6 +403,7 @@ var EcwtFactory = class {
|
|
|
360
403
|
ttl_initial
|
|
361
404
|
}) {
|
|
362
405
|
if (this.#redisClient) {
|
|
406
|
+
ttl_initial = ttl_initial ?? Number.MAX_SAFE_INTEGER;
|
|
363
407
|
const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
|
|
364
408
|
if (ts_ms_expired > Date.now()) {
|
|
365
409
|
await this.#redisClient.sendCommand([
|
|
@@ -385,5 +429,8 @@ var EcwtFactory = class {
|
|
|
385
429
|
// Annotate the CommonJS export names for ESM import in node:
|
|
386
430
|
0 && (module.exports = {
|
|
387
431
|
Ecwt,
|
|
388
|
-
|
|
432
|
+
EcwtExpiredError,
|
|
433
|
+
EcwtFactory,
|
|
434
|
+
EcwtInvalidError,
|
|
435
|
+
EcwtRevokedError
|
|
389
436
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ecwt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.2",
|
|
4
4
|
"description": "Encrypted CBOR-encoded Web Token",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"evilcrypt": "0.2.0-beta.4"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@kirick/snowflake": "^0.2.
|
|
22
|
+
"@kirick/snowflake": "^0.2.1-beta.1",
|
|
23
23
|
"lru-cache": "^7 || ^8 || ^9 || ^10",
|
|
24
24
|
"redis": "^4"
|
|
25
25
|
},
|
package/src/factory.js
CHANGED
|
@@ -137,19 +137,17 @@ export class EcwtFactory {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
|| ttl === Number.POSITIVE_INFINITY
|
|
140
|
+
typeof ttl !== 'number'
|
|
141
|
+
&& Number.isNaN(ttl) !== true
|
|
142
|
+
&& ttl !== null
|
|
145
143
|
) {
|
|
146
|
-
|
|
144
|
+
throw new TypeError('TTL must be a number or null.');
|
|
147
145
|
}
|
|
148
146
|
|
|
149
147
|
const snowflake = await this.#snowflakeFactory.createSafe();
|
|
150
148
|
|
|
151
149
|
const token_raw = cborEncode([
|
|
152
|
-
snowflake.
|
|
150
|
+
snowflake.toBuffer(),
|
|
153
151
|
ttl,
|
|
154
152
|
payload,
|
|
155
153
|
]);
|
|
@@ -299,6 +297,8 @@ export class EcwtFactory {
|
|
|
299
297
|
ttl_initial,
|
|
300
298
|
}) {
|
|
301
299
|
if (this.#redisClient) {
|
|
300
|
+
ttl_initial = ttl_initial ?? Number.MAX_SAFE_INTEGER;
|
|
301
|
+
|
|
302
302
|
const ts_ms_expired = ts_ms_created + (ttl_initial * 1000);
|
|
303
303
|
if (ts_ms_expired > Date.now()) {
|
|
304
304
|
await this.#redisClient.sendCommand([
|
package/src/main.js
CHANGED
package/src/token.js
CHANGED
|
@@ -2,24 +2,70 @@
|
|
|
2
2
|
import { toSeconds } from './utils/time.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @typedef {import('@kirick/snowflake
|
|
5
|
+
* @typedef {import('@kirick/snowflake').Snowflake} Snowflake
|
|
6
6
|
* @typedef {import('./factory.js').EcwtFactory} EcwtFactory
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Assigns property to object.
|
|
11
|
+
* @param {object} target -
|
|
12
|
+
* @param {string} key -
|
|
13
|
+
* @param {any} value -
|
|
14
|
+
*/
|
|
15
|
+
function assign(target, key, value) {
|
|
16
|
+
Object.defineProperty(
|
|
17
|
+
target,
|
|
18
|
+
key,
|
|
19
|
+
{
|
|
20
|
+
value,
|
|
21
|
+
enumerable: true,
|
|
22
|
+
writable: false,
|
|
23
|
+
configurable: false,
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
export class Ecwt {
|
|
10
29
|
#ecwtFactory;
|
|
11
|
-
|
|
12
|
-
#token;
|
|
13
|
-
#snowflake;
|
|
14
30
|
#ttl_initial;
|
|
15
|
-
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Token string representation.
|
|
34
|
+
* @type {string}
|
|
35
|
+
* @readonly
|
|
36
|
+
*/
|
|
37
|
+
token;
|
|
38
|
+
/**
|
|
39
|
+
* Token ID.
|
|
40
|
+
* @type {string}
|
|
41
|
+
* @readonly
|
|
42
|
+
*/
|
|
43
|
+
id;
|
|
44
|
+
/**
|
|
45
|
+
* Snowflake associated with token.
|
|
46
|
+
* @type {Snowflake}
|
|
47
|
+
* @readonly
|
|
48
|
+
*/
|
|
49
|
+
snowflake;
|
|
50
|
+
/**
|
|
51
|
+
* Timestamp when token expires in seconds.
|
|
52
|
+
* @type {number?}
|
|
53
|
+
* @readonly
|
|
54
|
+
*/
|
|
55
|
+
ts_expired;
|
|
56
|
+
/**
|
|
57
|
+
* Data stored in token.
|
|
58
|
+
* @type {{ [key: string]: any }}
|
|
59
|
+
* @readonly
|
|
60
|
+
*/
|
|
61
|
+
data;
|
|
16
62
|
|
|
17
63
|
/**
|
|
18
64
|
* @param {EcwtFactory} ecwtFactory -
|
|
19
65
|
* @param {object} options -
|
|
20
66
|
* @param {string} options.token String representation of token.
|
|
21
67
|
* @param {Snowflake} options.snowflake -
|
|
22
|
-
* @param {number
|
|
68
|
+
* @param {number?} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
23
69
|
* @param {object} options.data Data stored in token.
|
|
24
70
|
*/
|
|
25
71
|
constructor(
|
|
@@ -33,53 +79,51 @@ export class Ecwt {
|
|
|
33
79
|
) {
|
|
34
80
|
this.#ecwtFactory = ecwtFactory;
|
|
35
81
|
|
|
36
|
-
this.#token = token;
|
|
37
|
-
this.#snowflake = snowflake;
|
|
38
82
|
this.#ttl_initial = ttl_initial;
|
|
39
|
-
this.#data = Object.freeze(data);
|
|
40
|
-
}
|
|
41
83
|
|
|
42
|
-
|
|
43
|
-
|
|
84
|
+
assign(this, 'token', token);
|
|
85
|
+
assign(
|
|
86
|
+
this,
|
|
87
|
+
'id',
|
|
88
|
+
snowflake.toBase62(),
|
|
89
|
+
);
|
|
90
|
+
assign(this, 'snowflake', snowflake);
|
|
91
|
+
assign(
|
|
92
|
+
this,
|
|
93
|
+
'ts_expired',
|
|
94
|
+
this.#getTimestampExpired(),
|
|
95
|
+
);
|
|
96
|
+
assign(
|
|
97
|
+
this,
|
|
98
|
+
'data',
|
|
99
|
+
Object.freeze(data),
|
|
100
|
+
);
|
|
44
101
|
}
|
|
45
102
|
|
|
46
|
-
|
|
47
|
-
return this.#snowflake.base62;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get snowflake() {
|
|
51
|
-
return this.#snowflake;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get ts_expired() {
|
|
103
|
+
#getTimestampExpired() {
|
|
55
104
|
if (this.#ttl_initial === null) {
|
|
56
|
-
return
|
|
105
|
+
return null;
|
|
57
106
|
}
|
|
58
107
|
|
|
59
|
-
return toSeconds(this
|
|
108
|
+
return toSeconds(this.snowflake.timestamp) + this.#ttl_initial;
|
|
60
109
|
}
|
|
61
110
|
|
|
62
111
|
/**
|
|
63
|
-
*
|
|
64
|
-
* @
|
|
65
|
-
* @readonly
|
|
112
|
+
* Actual time to live in seconds.
|
|
113
|
+
* @returns {number | null} -
|
|
66
114
|
*/
|
|
67
|
-
|
|
115
|
+
getTTL() {
|
|
68
116
|
if (this.#ttl_initial === null) {
|
|
69
|
-
return
|
|
117
|
+
return null;
|
|
70
118
|
}
|
|
71
119
|
|
|
72
|
-
return this.#ttl_initial - toSeconds(Date.now() - this
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
get data() {
|
|
76
|
-
return this.#data;
|
|
120
|
+
return this.#ttl_initial - toSeconds(Date.now() - this.snowflake.timestamp);
|
|
77
121
|
}
|
|
78
122
|
|
|
79
123
|
/* async */ revoke() {
|
|
80
124
|
return this.#ecwtFactory._revoke({
|
|
81
125
|
token_id: this.id,
|
|
82
|
-
ts_ms_created: this
|
|
126
|
+
ts_ms_created: this.snowflake.timestamp,
|
|
83
127
|
ttl_initial: this.#ttl_initial,
|
|
84
128
|
});
|
|
85
129
|
}
|