ecwt 0.1.0-beta.2 → 0.1.0-beta.3
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 +232 -1
- package/dist/ecwt.cjs +1 -1
- package/package.json +2 -2
- package/src/factory.js +3 -3
package/README.md
CHANGED
|
@@ -1,3 +1,234 @@
|
|
|
1
1
|
|
|
2
|
-
#
|
|
2
|
+
# ECWT
|
|
3
3
|
Encrypted CBOR-encoded Web Token
|
|
4
|
+
|
|
5
|
+
## What is it?
|
|
6
|
+
|
|
7
|
+
ECWT is module for creating and verifying encrypted CBOR-encoded Web Tokens. It is designed to be used in situations where JWT is used, but there are major differences:
|
|
8
|
+
|
|
9
|
+
| | JWT | ECWT |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| Encoding | 🧐 JSON with base64 | ✅ CBOR <br> 2x smaller output |
|
|
12
|
+
| Binary data | 🧐 Double base64 encoding | ✅ Supported out of the box |
|
|
13
|
+
| Security | 📝 Signed <br> Payload is readable by everyone | 🔒 Encrypted <br> Payload is readable only by the private key possessor |
|
|
14
|
+
| Metadata | ➕ Type and algorithm, increases size | ✅ No unnecessary metadata |
|
|
15
|
+
| Revocation | 🧑💻 Requires additional implementation | ✅ Included with Redis |
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
ECWT depends on other modules, so you need to install them too.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
npm install ecwt @kirick/snowflake
|
|
23
|
+
pnpm install ecwt @kirick/snowflake
|
|
24
|
+
bun install ecwt @kirick/snowflake
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Some dependencies
|
|
28
|
+
|
|
29
|
+
`EcwtFactory` depends on other modules, so you might be need to install them too.
|
|
30
|
+
|
|
31
|
+
#### `@kirick/snowflake` to create unique IDs (required)
|
|
32
|
+
|
|
33
|
+
For documentation, see [snowflake-js repository](https://github.com/kirick13/snowflake-js).
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
import { SnowflakeFactory } from '@kirick/snowflake';
|
|
37
|
+
|
|
38
|
+
const snowflakeFactory = new SnowflakeFactory({
|
|
39
|
+
server_id: 0,
|
|
40
|
+
worker_id: 0,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
#### `redis` to store revoked tokens (optional)
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
import { createClient } from 'redis';
|
|
48
|
+
|
|
49
|
+
const redisClient = createClient({
|
|
50
|
+
socket: {
|
|
51
|
+
host: 'localhost',
|
|
52
|
+
port: 6379,
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await redisClient.connect();
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### `lru` to avoid decrypt the same token multiple times (optional)
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
import { LRUCache } from 'lru-cache';
|
|
63
|
+
|
|
64
|
+
const lruCache = new LRUCache({
|
|
65
|
+
max: 1000, // maximum of 1000 items
|
|
66
|
+
ttl: 60 * 60 * 1000, // 1 hour
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### Validation library of your choice (optional)
|
|
71
|
+
|
|
72
|
+
To reduce the size of the token, ECWT does not include object keys in the payload. By specifying the schema, you also can validate the payloads.
|
|
73
|
+
|
|
74
|
+
In our example, we use [valibot](https://valibot.dev) library.
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
import {
|
|
78
|
+
number,
|
|
79
|
+
string,
|
|
80
|
+
maxValue,
|
|
81
|
+
maxLength,
|
|
82
|
+
safeParse } from 'valibot';
|
|
83
|
+
|
|
84
|
+
const schema = {
|
|
85
|
+
user_id: (value) => safeParse(
|
|
86
|
+
number([
|
|
87
|
+
maxValue(10),
|
|
88
|
+
]),
|
|
89
|
+
value,
|
|
90
|
+
).success,
|
|
91
|
+
nick: (value) => safeParse(
|
|
92
|
+
string([
|
|
93
|
+
maxLength(10),
|
|
94
|
+
]),
|
|
95
|
+
value,
|
|
96
|
+
).success,
|
|
97
|
+
};
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
That schema will prevent creating tokens for users with ID greater than 10 and nicknames longer than 10 characters.
|
|
101
|
+
|
|
102
|
+
## API
|
|
103
|
+
|
|
104
|
+
### `EcwtFactory`
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
constructor({
|
|
108
|
+
redisClient: RedisClientType?,
|
|
109
|
+
lruCache: LRU?,
|
|
110
|
+
snowflakeFactory: SnowflakeFactory,
|
|
111
|
+
options: {
|
|
112
|
+
namespace: string?,
|
|
113
|
+
key: Buffer,
|
|
114
|
+
schema: {
|
|
115
|
+
[key: string]: (value: any) => boolean,
|
|
116
|
+
} = {},
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Create your `EcwtFactory` instance to create, and parse tokens.
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
import { EcwtFactory } from 'ecwt';
|
|
125
|
+
|
|
126
|
+
const ecwtFactory = new EcwtFactory({
|
|
127
|
+
redisClient,
|
|
128
|
+
lruCache,
|
|
129
|
+
snowflakeFactory,
|
|
130
|
+
options: {
|
|
131
|
+
// "options.namespace" is required to identify the storage of revoked tokens in Redis
|
|
132
|
+
namespace: 'test',
|
|
133
|
+
key: Buffer.from(
|
|
134
|
+
'54RoavO+7orGGCKqLXcMwNGFGbcnSEq22f9bJX3lT9lgEPSaRAMBaEnHgMQPTPXcifFvGZmDGzOFqUMfqXsAhQ==',
|
|
135
|
+
'base64',
|
|
136
|
+
),
|
|
137
|
+
schema,
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Class method `create`
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
create(
|
|
146
|
+
payload: {
|
|
147
|
+
[key: string]: any,
|
|
148
|
+
},
|
|
149
|
+
options: {
|
|
150
|
+
ttl: number?,
|
|
151
|
+
}
|
|
152
|
+
): Promise<Ecwt>
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Creates a token.
|
|
156
|
+
|
|
157
|
+
`options.ttl` specifies the time to live of the token in seconds. If not specified, the token will not expire.
|
|
158
|
+
|
|
159
|
+
Returns `Ecwt` instance.
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
const ecwt_token = await ecwtFactory.create(
|
|
163
|
+
{
|
|
164
|
+
user_id: 1,
|
|
165
|
+
nick: 'kirick',
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
ttl: 30 * 60,
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
#### Class method `verify`
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
verify(
|
|
177
|
+
token: string,
|
|
178
|
+
): Promise<Ecwt>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Parses string representation of the token and verifies it:
|
|
182
|
+
|
|
183
|
+
- to be decrypted properly,
|
|
184
|
+
- for expiration,
|
|
185
|
+
- for revocation (if Redis client is provided),
|
|
186
|
+
- for schema.
|
|
187
|
+
|
|
188
|
+
Returns `Ecwt` instance.
|
|
189
|
+
|
|
190
|
+
If the token is invalid, throws `EcwtInvalidError` which contains `Ecwt` instance in the `ecwt` property.
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
const ecwt_token = await ecwtFactory.verify(token);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### `Ecwt`
|
|
197
|
+
|
|
198
|
+
Represents the token. It cannot be created by the user.
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
import { Ecwt } from 'ecwt';
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Class property `token: string`
|
|
205
|
+
|
|
206
|
+
The string representation of the token.
|
|
207
|
+
|
|
208
|
+
#### Class property `id: string`
|
|
209
|
+
|
|
210
|
+
The unique ID of the token.
|
|
211
|
+
|
|
212
|
+
#### Class property `snowflake: Snowflake`
|
|
213
|
+
|
|
214
|
+
The `Snowflake` instance of the token. For documentation, see [snowflake-js repository](https://github.com/kirick13/snowflake-js).
|
|
215
|
+
|
|
216
|
+
#### Class property `ts_expired: number`
|
|
217
|
+
|
|
218
|
+
The timestamp of the token expiration in seconds. If the token does not expire, it is `Number.POSITIVE_INFINITY`.
|
|
219
|
+
|
|
220
|
+
#### Class property `ttl: number`
|
|
221
|
+
|
|
222
|
+
Current the time to live of the token in seconds. If the token does not expire, it is `Number.POSITIVE_INFINITY`.
|
|
223
|
+
|
|
224
|
+
#### Class property `data: { [key: string]: any }`
|
|
225
|
+
|
|
226
|
+
The payload of the token.
|
|
227
|
+
|
|
228
|
+
#### Class method `revoke`
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
revoke(): Promise<void>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Revokes the token. It will be impossible to verify it after that.
|
package/dist/ecwt.cjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ecwt",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"description": "Encrypted CBOR-encoded Web Token",
|
|
5
5
|
"main": "src/main.js",
|
|
6
6
|
"type": "module",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"base-x": "4.0.0",
|
|
18
18
|
"cbor-x": "1.5.6",
|
|
19
|
-
"evilcrypt": "0.2.0-beta.
|
|
19
|
+
"evilcrypt": "0.2.0-beta.4"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
22
|
"@kirick/snowflake": "^0.2.0-beta.7",
|
package/src/factory.js
CHANGED
|
@@ -4,8 +4,8 @@ import {
|
|
|
4
4
|
encode as cborEncode,
|
|
5
5
|
decode as cborDecode } from 'cbor-x';
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
decrypt as evilcryptDecrypt,
|
|
8
|
+
v2 as evilcryptV2 } from 'evilcrypt';
|
|
9
9
|
import { LRUCache } from 'lru-cache';
|
|
10
10
|
import { createClient } from 'redis';
|
|
11
11
|
import { Ecwt } from './token.js';
|
|
@@ -154,7 +154,7 @@ export class EcwtFactory {
|
|
|
154
154
|
payload,
|
|
155
155
|
]);
|
|
156
156
|
|
|
157
|
-
const token_encrypted = await
|
|
157
|
+
const token_encrypted = await evilcryptV2.encrypt(
|
|
158
158
|
token_raw,
|
|
159
159
|
this.#encryption_key,
|
|
160
160
|
);
|