ecwt 0.1.0-beta.3 → 0.1.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/README.md +21 -12
- package/dist/ecwt.cjs +73 -32
- package/package.json +2 -2
- package/src/factory.js +7 -7
- package/src/token.js +78 -34
package/README.md
CHANGED
|
@@ -147,18 +147,23 @@ 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. But such 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
|
|
166
|
+
// Example
|
|
162
167
|
const ecwt_token = await ecwtFactory.create(
|
|
163
168
|
{
|
|
164
169
|
user_id: 1,
|
|
@@ -201,29 +206,33 @@ Represents the token. It cannot be created by the user.
|
|
|
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
|
@@ -47,18 +47,57 @@ function toSeconds(value) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// src/token.js
|
|
50
|
+
function assign(target, key, value) {
|
|
51
|
+
Object.defineProperty(
|
|
52
|
+
target,
|
|
53
|
+
key,
|
|
54
|
+
{
|
|
55
|
+
value,
|
|
56
|
+
enumerable: true,
|
|
57
|
+
writable: false,
|
|
58
|
+
configurable: false
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
}
|
|
50
62
|
var Ecwt = class {
|
|
51
63
|
#ecwtFactory;
|
|
52
|
-
#token;
|
|
53
|
-
#snowflake;
|
|
54
64
|
#ttl_initial;
|
|
55
|
-
|
|
65
|
+
/**
|
|
66
|
+
* Token string representation.
|
|
67
|
+
* @type {string}
|
|
68
|
+
* @readonly
|
|
69
|
+
*/
|
|
70
|
+
token;
|
|
71
|
+
/**
|
|
72
|
+
* Token ID.
|
|
73
|
+
* @type {string}
|
|
74
|
+
* @readonly
|
|
75
|
+
*/
|
|
76
|
+
id;
|
|
77
|
+
/**
|
|
78
|
+
* Snowflake associated with token.
|
|
79
|
+
* @type {Snowflake}
|
|
80
|
+
* @readonly
|
|
81
|
+
*/
|
|
82
|
+
snowflake;
|
|
83
|
+
/**
|
|
84
|
+
* Timestamp when token expires in seconds.
|
|
85
|
+
* @type {number?}
|
|
86
|
+
* @readonly
|
|
87
|
+
*/
|
|
88
|
+
ts_expired;
|
|
89
|
+
/**
|
|
90
|
+
* Data stored in token.
|
|
91
|
+
* @type {{ [key: string]: any }}
|
|
92
|
+
* @readonly
|
|
93
|
+
*/
|
|
94
|
+
data;
|
|
56
95
|
/**
|
|
57
96
|
* @param {EcwtFactory} ecwtFactory -
|
|
58
97
|
* @param {object} options -
|
|
59
98
|
* @param {string} options.token String representation of token.
|
|
60
99
|
* @param {Snowflake} options.snowflake -
|
|
61
|
-
* @param {number
|
|
100
|
+
* @param {number?} options.ttl_initial Time to live in seconds at the moment of token creation.
|
|
62
101
|
* @param {object} options.data Data stored in token.
|
|
63
102
|
*/
|
|
64
103
|
constructor(ecwtFactory, {
|
|
@@ -68,45 +107,46 @@ var Ecwt = class {
|
|
|
68
107
|
data
|
|
69
108
|
}) {
|
|
70
109
|
this.#ecwtFactory = ecwtFactory;
|
|
71
|
-
this.#token = token;
|
|
72
|
-
this.#snowflake = snowflake;
|
|
73
110
|
this.#ttl_initial = ttl_initial;
|
|
74
|
-
this
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
111
|
+
assign(this, "token", token);
|
|
112
|
+
assign(
|
|
113
|
+
this,
|
|
114
|
+
"id",
|
|
115
|
+
snowflake.toBase62()
|
|
116
|
+
);
|
|
117
|
+
assign(this, "snowflake", snowflake);
|
|
118
|
+
assign(
|
|
119
|
+
this,
|
|
120
|
+
"ts_expired",
|
|
121
|
+
this.#getTimestampExpired()
|
|
122
|
+
);
|
|
123
|
+
assign(
|
|
124
|
+
this,
|
|
125
|
+
"data",
|
|
126
|
+
Object.freeze(data)
|
|
127
|
+
);
|
|
84
128
|
}
|
|
85
|
-
|
|
129
|
+
#getTimestampExpired() {
|
|
86
130
|
if (this.#ttl_initial === null) {
|
|
87
|
-
return
|
|
131
|
+
return null;
|
|
88
132
|
}
|
|
89
|
-
return toSeconds(this
|
|
133
|
+
return toSeconds(this.snowflake.timestamp) + this.#ttl_initial;
|
|
90
134
|
}
|
|
91
135
|
/**
|
|
92
|
-
*
|
|
93
|
-
* @
|
|
94
|
-
* @readonly
|
|
136
|
+
* Actual time to live in seconds.
|
|
137
|
+
* @returns {number | null} -
|
|
95
138
|
*/
|
|
96
|
-
|
|
139
|
+
getTTL() {
|
|
97
140
|
if (this.#ttl_initial === null) {
|
|
98
|
-
return
|
|
141
|
+
return null;
|
|
99
142
|
}
|
|
100
|
-
return this.#ttl_initial - toSeconds(Date.now() - this
|
|
101
|
-
}
|
|
102
|
-
get data() {
|
|
103
|
-
return this.#data;
|
|
143
|
+
return this.#ttl_initial - toSeconds(Date.now() - this.snowflake.timestamp);
|
|
104
144
|
}
|
|
105
145
|
/* async */
|
|
106
146
|
revoke() {
|
|
107
147
|
return this.#ecwtFactory._revoke({
|
|
108
148
|
token_id: this.id,
|
|
109
|
-
ts_ms_created: this
|
|
149
|
+
ts_ms_created: this.snowflake.timestamp,
|
|
110
150
|
ttl_initial: this.#ttl_initial
|
|
111
151
|
});
|
|
112
152
|
}
|
|
@@ -233,12 +273,12 @@ var EcwtFactory = class {
|
|
|
233
273
|
}
|
|
234
274
|
payload.push(value);
|
|
235
275
|
}
|
|
236
|
-
if (typeof ttl !== "number" && Number.isNaN(ttl) !== true
|
|
237
|
-
|
|
276
|
+
if (typeof ttl !== "number" && Number.isNaN(ttl) !== true && ttl !== null) {
|
|
277
|
+
throw new TypeError("TTL must be a number or null.");
|
|
238
278
|
}
|
|
239
279
|
const snowflake = await this.#snowflakeFactory.createSafe();
|
|
240
280
|
const token_raw = (0, import_cbor_x.encode)([
|
|
241
|
-
snowflake.
|
|
281
|
+
snowflake.toBuffer(),
|
|
242
282
|
ttl,
|
|
243
283
|
payload
|
|
244
284
|
]);
|
|
@@ -360,6 +400,7 @@ var EcwtFactory = class {
|
|
|
360
400
|
ttl_initial
|
|
361
401
|
}) {
|
|
362
402
|
if (this.#redisClient) {
|
|
403
|
+
ttl_initial = ttl_initial ?? Number.MAX_SAFE_INTEGER;
|
|
363
404
|
const ts_ms_expired = ts_ms_created + ttl_initial * 1e3;
|
|
364
405
|
if (ts_ms_expired > Date.now()) {
|
|
365
406
|
await this.#redisClient.sendCommand([
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ecwt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.1",
|
|
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/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
|
}
|