Skip to content

Commit 4d2c8db

Browse files
authored
Merge pull request #659 from nebarf/feat/jsonwebtoken
feat(jsonwebtoken): jsonwebtoken implementation
2 parents 43a40ea + 879108a commit 4d2c8db

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1956
-7
lines changed

.gitattributes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ packages/deno-lint/index.js linguist-detectable=false
2020
packages/deno-lint/index.d.ts linguist-detectable=false
2121
packages/jieba/index.js linguist-detectable=false
2222
packages/jieba/index.d.ts linguist-detectable=false
23+
packages/jsonwebtoken/index.js linguist-detectable=false
24+
packages/jsonwebtoken/index.d.ts linguist-detectable=false
2325
packages/xxhash/index.js linguist-detectable=false
2426
packages/xxhash/index.d.ts linguist-detectable=false
2527
.yarn/releases/*.js linguist-detectable=false

.github/workflows/ci.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ jobs:
9191
yarn lerna exec "yarn build --target aarch64-unknown-linux-musl" --concurrency 1 --stream --no-prefix
9292
- host: windows-latest
9393
target: 'aarch64-pc-windows-msvc'
94-
build: yarn lerna exec "yarn build --target aarch64-pc-windows-msvc" --concurrency 1 --stream --no-prefix
94+
build: yarn lerna exec "yarn build --target aarch64-pc-windows-msvc" --ignore @node-rs/jsonwebtoken --concurrency 1 --stream --no-prefix
9595

9696
name: stable - ${{ matrix.settings.target }} - node@18
9797
runs-on: ${{ matrix.settings.host }}
@@ -111,7 +111,7 @@ jobs:
111111
uses: dtolnay/rust-toolchain@stable
112112
if: ${{ !matrix.settings.docker }}
113113
with:
114-
toolchain: nightly-2023-01-11
114+
toolchain: nightly-2023-03-01
115115
targets: ${{ matrix.settings.target }}
116116

117117
- name: Cache cargo registry
@@ -132,7 +132,7 @@ jobs:
132132
- uses: goto-bus-stop/setup-zig@v2
133133
if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' }}
134134
with:
135-
version: 0.10.0
135+
version: 0.10.1
136136

137137
- name: Setup node x86
138138
if: matrix.settings.target == 'i686-pc-windows-msvc'
@@ -276,6 +276,7 @@ jobs:
276276
yarn test packages/bcrypt
277277
yarn test packages/crc32
278278
yarn test packages/jieba
279+
yarn test packages/jsonwebtoken
279280
yarn test packages/xxhash
280281
281282
test-linux-x64-gnu-binding:

.github/workflows/lint.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
- name: Install
2424
uses: dtolnay/rust-toolchain@stable
2525
with:
26-
toolchain: nightly-2023-01-11
26+
toolchain: nightly-2023-03-01
2727
components: rustfmt, clippy
2828

2929
- name: 'Install dependencies'

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ members = [
88
"./packages/crc32",
99
"./packages/deno-lint",
1010
"./packages/jieba",
11+
"./packages/jsonwebtoken",
1112
"./packages/xxhash",
1213
]
1314

packages/jsonwebtoken/Cargo.toml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[package]
2+
authors = ["Francesco Benedetto"]
3+
edition = "2021"
4+
name = "node-rs-jsonwebtoken"
5+
version = "0.1.0"
6+
7+
[lib]
8+
crate-type = ["cdylib"]
9+
10+
[dependencies]
11+
global_alloc = { path = "../../crates/alloc" }
12+
jsonwebtoken = { version = "8" }
13+
napi = { version = "2", default-features = false, features = [
14+
"napi3",
15+
"serde-json",
16+
] }
17+
napi-derive = { version = "2" }
18+
serde = "1.0"
19+
serde_json = "1.0"
20+
rand = "0.8"
21+
22+
[build-dependencies]
23+
napi-build = "2"

packages/jsonwebtoken/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# `@node-rs/jsonwebtoken`
2+
3+
![](https://github.com/napi-rs/node-rs/workflows/CI/badge.svg)
4+
![](https://img.shields.io/npm/dm/@node-rs/jsonwebtoken.svg?sanitize=true)
5+
6+
🚀 Fastest jsonwebtoken in Node.js
7+
8+
## Support matrix
9+
10+
| | node12 | node14 | node16 | node18 |
11+
| ---------------- | ------ | ------ | ------ | ------ |
12+
| Windows x64 |||||
13+
| Windows x32 |||||
14+
| Windows arm64 |||||
15+
| macOS x64 |||||
16+
| macOS arm64 |||||
17+
| Linux x64 gnu |||||
18+
| Linux x64 musl |||||
19+
| Linux arm gnu |||||
20+
| Linux arm64 gnu |||||
21+
| Linux arm64 musl |||||
22+
| Android arm64 |||||
23+
| Android armv7 |||||
24+
| FreeBSD x64 |||||
25+
26+
## Usage
27+
28+
See [tests](__tests__/jsonwebtoken.spec.ts) and [types](index.d.ts)
29+
30+
## Bench
31+
32+
```
33+
Model Name: MacBook Pro
34+
Model Identifier: MacBookPro18,2
35+
Processor Name: Apple M1 Max
36+
Processor Speed: 2.6 GHz
37+
Number of Processors: 1
38+
Total Number of Cores: 8
39+
L2 Cache (per Core): 256 KB
40+
L3 Cache: 12 MB
41+
Hyper-Threading Technology: Disabled
42+
Memory: 64 GB
43+
```
44+
45+
```text
46+
@node-rs/jsonwebtoken x 17,491 ops/sec ±0.39% (92 runs sampled)
47+
node-jsonwebtoken x 6,899 ops/sec ±0.62% (88 runs sampled)
48+
Async sign bench suite: Fastest is @node-rs/jsonwebtoken
49+
50+
@node-rs/jsonwebtoken x 17,393 ops/sec ±1.57% (87 runs sampled)
51+
node-jsonwebtoken x 5,256 ops/sec ±0.74% (85 runs sampled)
52+
Async verify bench suite: Fastest is @node-rs/jsonwebtoken
53+
54+
@node-rs/jsonwebtoken x 264,001 ops/sec ±0.08% (101 runs sampled)
55+
node-jsonwebtoken x 71,785 ops/sec ±1.01% (97 runs sampled)
56+
Sync sign bench suite: Fastest is @node-rs/jsonwebtoken
57+
58+
@node-rs/jsonwebtoken x 278,657 ops/sec ±0.08% (99 runs sampled)
59+
node-jsonwebtoken x 54,462 ops/sec ±0.53% (100 runs sampled)
60+
Sync verify bench suite: Fastest is @node-rs/jsonwebtoken
61+
```
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { promises as fs } from 'node:fs'
2+
import { join } from 'node:path'
3+
4+
import test from 'ava'
5+
import { decode as nodeJwtDecode } from 'jsonwebtoken'
6+
7+
import { Algorithm, sign, signSync, verifySync, verify } from '../index.js'
8+
9+
const getUtcTimestamp = () => Math.floor(new Date().getTime() / 1000)
10+
const oneDayInSeconds = 86400
11+
12+
test('signSync and sign (async) should produce the same result', async (t) => {
13+
const claims = {
14+
data: {
15+
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
16+
pr: 33,
17+
isM: true,
18+
set: ['KL', 'TV', 'JI'],
19+
nest: { id: 'poly' },
20+
},
21+
exp: getUtcTimestamp() + oneDayInSeconds,
22+
}
23+
const secretKey = 'secret'
24+
const resSync = signSync(claims, secretKey)
25+
const resAsync = await sign(claims, secretKey)
26+
27+
t.is(resSync, resAsync)
28+
t.truthy(nodeJwtDecode(resAsync))
29+
})
30+
31+
test('verify should return the decoded claims', async (t) => {
32+
const data = {
33+
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
34+
pr: 33,
35+
isM: true,
36+
set: ['KL', 'TV', 'JI'],
37+
nest: { id: 'poly' },
38+
}
39+
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
40+
const secretKey = 'secret'
41+
const headers = { algorithm: Algorithm.HS384 }
42+
43+
const token = await sign(claims, secretKey, headers)
44+
45+
const validation = { algorithms: [Algorithm.HS384] }
46+
const decodedToken = await verify(token, secretKey, validation)
47+
48+
t.like(decodedToken, claims)
49+
})
50+
51+
test('verify sync should return the decoded claims', async (t) => {
52+
const data = {
53+
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
54+
pr: 33,
55+
isM: true,
56+
set: ['KL', 'TV', 'JI'],
57+
nest: { id: 'poly' },
58+
}
59+
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
60+
const publicKeyPem =
61+
'-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzq7L/V03tpy3QTYOP51CT0fY2Sp5spejcja9brkEZoLYFcvLSeNnsXtPg/Sr7PwbykiXoY++xo7+6o2VfPnbiEFV8fNap+4tWDmxeZfPifmCEA58BFncnK8z5luxR+syeRuI/9IUHllGxsKoQAbFECZoNCR+I5H/ynqhm9rvk89iNsh5EGxknOq2GmMaKRZ3nHZtVuwUj3BlwgsmP28ZAofMN/xM8bugHS1nNNHmRh6Ubg0Od3r2FH0+3df86ZzJ013M/LG1189aGNPXDOH4guBh7TPficw9nUnhIghiEFrxhAvIOQjClbhFud931T+UqD5BsF/ZarJ1VkaUa3UjxwIDAQAB-----END PUBLIC KEY-----'
62+
const privateKeyPem =
63+
'-----BEGIN RSA PRIVATE KEY-----MIIEowIBAAKCAQEAzq7L/V03tpy3QTYOP51CT0fY2Sp5spejcja9brkEZoLYFcvLSeNnsXtPg/Sr7PwbykiXoY++xo7+6o2VfPnbiEFV8fNap+4tWDmxeZfPifmCEA58BFncnK8z5luxR+syeRuI/9IUHllGxsKoQAbFECZoNCR+I5H/ynqhm9rvk89iNsh5EGxknOq2GmMaKRZ3nHZtVuwUj3BlwgsmP28ZAofMN/xM8bugHS1nNNHmRh6Ubg0Od3r2FH0+3df86ZzJ013M/LG1189aGNPXDOH4guBh7TPficw9nUnhIghiEFrxhAvIOQjClbhFud931T+UqD5BsF/ZarJ1VkaUa3UjxwIDAQABAoIBAQDGYRB7B9ZJ+PIMLY5PkOnsntGM4DAfM102a0Q32m5W1pABm7JsIVGOEQWpalb7CKDD8BlagVZjzyzuhSdO5aPJjKyppyMEvJ/ZZsbqJsSVcl9cegqfQoF2AtSV7ryigyXXCI7evQ2Cc75zWLOVgOn1LmgmZECOc7xI5JvptKLwAwrIuLE4wnuLgSdxxVZ8uwJHW7+hTCQ8x0cSID1POy3q39kEEdqi+yNOrVFZV7DGJ6T5gYWDe53fWpks++tr7D6Wbq1mRdX5T62IdG/G4q9/vA2tSR+5hZWMxMqZ+GUBmIH2zPU16yc4hfwne/C5WQkRUaPBIl/u5swFLHwxNIqBAoGBAPsCOB5T7/oO9Y/LyD6SCDLiKpKQhwPPZJ76/Nu9yNXM2sLINGDq6RUXmaflifoKSRxFqApBHXqcP8NRzrYT+eY5Q0/m2Nvt4MvoMRoNDx2FVnQY8yo4AdSpQl2fNhMdXc1R2Wc3EJdWZd+2J9xGBTbLZ5nUem9zdVdZr0YbMrwpAoGBANLK7txwi/YSYfHo+S0KZqqO32CAN8m0s6Clnz1SomZY4TX1nQQyfbzT06AG/7vtVf5roc4t1JrX08Qelu7VBOCH2Y2jEYyX1M6e7sJbl+Z5LYqOQkiAW+GBF3gvn/IvQ1Irjzd8MF/5wfyafaeE5mxoAtDOGW/BfcwORIoAOt5vAoGAHHjx+K64x/qubDNHcaGLAIqbHaj7R7lcxpPd3uc2QtpL7lBbcKr06YmVym/FKPHFvUlBeHhOabwTl4pOEmVNsYnJUuTysG/ZUgfymevlTQn09pJl8uILgx34AzquHZj1LPcd3BFo9mG8iJXXC6t9p+uGwvJRORc1tkTcFu264ZECgYB9sygXakH8PmAL6vrUQhSQ9tv75tndvZU0Yi+AWQug7rV2AP5eJ2HVvZfAIQxVW6VhL3vwwGG86KFOnVMyHvNmlXxFOw3XAh+UCzCj1AzUEkT3D/g01d50rg95yySdPlPt5y3jT3plcUGdyd7Oi7EAylGLhKukegTzLzrt9E8mnwKBgBx+31YGB/sxdLXKN7CKvkB9+PUQ1ywDZshzuXfSL+lEcgls6MT/AjMP49eEu14698S4VHnhMu/671TwJXS6NpCTCGjrUJoKymuaBGYvgFRqcqjVtHzyz+YMkFQISvi/DurN5CN4C1Yiv7EDFQC+69fcOo4tP9S9EFya189IvJsJ-----END RSA PRIVATE KEY-----'
64+
const headers = { algorithm: Algorithm.RS256 }
65+
66+
const token = await sign(claims, privateKeyPem, headers)
67+
68+
const validation = { algorithms: [Algorithm.RS256] }
69+
const decodedToken = verifySync(token, publicKeyPem, validation)
70+
71+
t.like(decodedToken, claims)
72+
})
73+
74+
test('verify should throw in case the token is expired', async (t) => {
75+
const data = {
76+
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
77+
pr: 33,
78+
isM: true,
79+
set: ['KL', 'TV', 'JI'],
80+
nest: { id: 'poly' },
81+
}
82+
const claims = { data, iat: getUtcTimestamp() - oneDayInSeconds * 2, exp: getUtcTimestamp() - oneDayInSeconds }
83+
const secretKey = 'secret'
84+
const headers = { algorithm: Algorithm.HS256 }
85+
86+
const token = await sign(claims, secretKey, headers)
87+
88+
t.throws(() => verifySync(token, secretKey))
89+
await t.throwsAsync(() => verify(token, secretKey))
90+
})
91+
92+
test('should allow to use a buffer as sign/verify key', async (t) => {
93+
const [publicKeyPem, privateKeyPem] = await Promise.all([
94+
fs.readFile(join(__dirname, 'public-key.pem')),
95+
fs.readFile(join(__dirname, 'private-key.pem')),
96+
])
97+
98+
const data = {
99+
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
100+
pr: 33,
101+
isM: true,
102+
set: ['KL', 'TV', 'JI'],
103+
nest: { id: 'poly' },
104+
}
105+
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
106+
const headers = { algorithm: Algorithm.PS256 }
107+
108+
const token = await sign(claims, privateKeyPem.toString(), headers)
109+
110+
const validation = { algorithms: [Algorithm.PS256] }
111+
const decodedToken = await verify(token, publicKeyPem.toString(), validation)
112+
113+
t.like(decodedToken, claims)
114+
})
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
-----BEGIN RSA PRIVATE KEY-----
2+
MIIEowIBAAKCAQEAowpInj/GxkrOtNVXG89JXmsglD495jzijM2xOEs4jIlKa+B2
3+
QVIY296+GJUJtnvUboEIGBYwWZUVxBnvVpGYB+0nYTnZSn7A9+KpJ8Lpt+ui/Ibc
4+
kfx6gw5CfSIeKUz5cIuaPxNIZByEUL4FI+6dQGVLc4eVWDCDRpR2RuOW1EcrbIEm
5+
CKrhQgpWrmpc7v8JYj9bzcE63R0iWM8EM9fawbu6yJxFxqQ9vwZFZBYw7vsdj+Pa
6+
9dNyhqTAtUr3c1+fZEDifYSEdGRJZld+2z7Pm2LEI7EFWRKC8LTHA4rC4N6U2B1k
7+
wRWZ2gk7aXu1W4gu9qD79OIjcuhkuVx8OrJZMwIDAQABAoIBAQCgBcPorqgmj936
8+
Vzq8LOPSLEs5tS2EAVZK5MiAfDPwm//TiegHjNChXSovbniuBzQlkbekDINAKbfH
9+
Vb03tocFoJr6LpE7MNWtd2aXhBNpVXoPaT6seqa0YxaXQxlfaBGbiSnHpuFygRrN
10+
NPROpDDrt4Aq0HSgrlzqtWSxh0fO6MqFrCZPfLelY+etDHfr+JkMG2qbEKYqv7ZF
11+
e18Pvf97VwFebNTolWCKE8A+G8Ps7nUrsl5OiDYNGK42kPZNbM0uImgGgIzvVdYs
12+
458WVwi4rA9A5vrzrANY9U+SMQg3A9Cr1DXMQBHb2MC0W/AxB5UWISEtNe99p7UH
13+
uqtoQUwhAoGBAM1vOWgksEhoJJUAW3UWknAP/SOEqMxDYFTXx2y9q1F7g9Dhe9gC
14+
+wqQQsXP0w2pGyBslFx8RWcP55uc/r1kcjbQbF0i2yWv8ZgVsloFh2WukBODNikp
15+
CDyj8YHSHHi1bojwBeIv32sLt1awBKaUKuJhYgonpliJfvf9Edpm4HYPAoGBAMsr
16+
uPHaWf1XjRjd4SpStwzIMBOtS49twriAZvqEBQcAHYhLldZJFpwLxYrwLk6eZK9E
17+
fQgQDANCl4ez5bfgNR11QCjLiziX1K++e1sERbSGTIV/U6rHeDveaCU+rGG672MZ
18+
P1mOl+trXtFcryfQ7tNyBfjD3E4jF00/yXs+OO6dAoGAMs/58QpyF9a6hahK9tEY
19+
c2NhB3H+lldr8lBU4U6gm0zjs7yx9yH1mg1IlsjquQxEy2ZP4/hQ6kcC0HiqgYng
20+
vjIbO4YtkkrMhQOI079eWAYvWMQxl0iw4t7iE2w24pxttK05p1KT/lQtiuGKpPEt
21+
EkVoDH72JBwOLaSIz+52Qn0CgYA1B7qEViv69mk7vl5RP7nLukziNe9tBoc2xT0b
22+
0m3FgAA1XRVFE1q1bFUpiLttheZd4RCJlDaueyk2IHyrW/hBMiOHAmnaYbcAEEX+
23+
YcUX853xkmRyRRJa/hhM8GjqMXLeeO6SH6gDqMjc+MY3LE/KHQ71+Zl9Q6eHYEjk
24+
xD7z+QKBgGC7MNG1EARfA4kgp7w+j5JBiCakZdXEzJFUzk994ZSBpY39bftecd/i
25+
0OKuVPXX1qcN3en8p+qAPOriukBITkG4IVQvH+SJVEpV61E3+C+bs9jmp3RtBoiS
26+
1/DWZxnwoqFYnzYLHEsKt4BwJ2fiSE0uUDGJdv+ZJhjSGt1R/fbJ
27+
-----END RSA PRIVATE KEY-----
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
-----BEGIN PUBLIC KEY-----
2+
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAowpInj/GxkrOtNVXG89J
3+
XmsglD495jzijM2xOEs4jIlKa+B2QVIY296+GJUJtnvUboEIGBYwWZUVxBnvVpGY
4+
B+0nYTnZSn7A9+KpJ8Lpt+ui/Ibckfx6gw5CfSIeKUz5cIuaPxNIZByEUL4FI+6d
5+
QGVLc4eVWDCDRpR2RuOW1EcrbIEmCKrhQgpWrmpc7v8JYj9bzcE63R0iWM8EM9fa
6+
wbu6yJxFxqQ9vwZFZBYw7vsdj+Pa9dNyhqTAtUr3c1+fZEDifYSEdGRJZld+2z7P
7+
m2LEI7EFWRKC8LTHA4rC4N6U2B1kwRWZ2gk7aXu1W4gu9qD79OIjcuhkuVx8OrJZ
8+
MwIDAQAB
9+
-----END PUBLIC KEY-----

0 commit comments

Comments
 (0)