Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ packages/deno-lint/index.js linguist-detectable=false
packages/deno-lint/index.d.ts linguist-detectable=false
packages/jieba/index.js linguist-detectable=false
packages/jieba/index.d.ts linguist-detectable=false
packages/jsonwebtoken/index.js linguist-detectable=false
packages/jsonwebtoken/index.d.ts linguist-detectable=false
packages/xxhash/index.js linguist-detectable=false
packages/xxhash/index.d.ts linguist-detectable=false
.yarn/releases/*.js linguist-detectable=false
7 changes: 4 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
yarn lerna exec "yarn build --target aarch64-unknown-linux-musl" --concurrency 1 --stream --no-prefix
- host: windows-latest
target: 'aarch64-pc-windows-msvc'
build: yarn lerna exec "yarn build --target aarch64-pc-windows-msvc" --concurrency 1 --stream --no-prefix
build: yarn lerna exec "yarn build --target aarch64-pc-windows-msvc" --ignore @node-rs/jsonwebtoken --concurrency 1 --stream --no-prefix

name: stable - ${{ matrix.settings.target }} - node@18
runs-on: ${{ matrix.settings.host }}
Expand All @@ -111,7 +111,7 @@ jobs:
uses: dtolnay/rust-toolchain@stable
if: ${{ !matrix.settings.docker }}
with:
toolchain: nightly-2023-01-11
toolchain: nightly-2023-03-01
targets: ${{ matrix.settings.target }}

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

- name: Setup node x86
if: matrix.settings.target == 'i686-pc-windows-msvc'
Expand Down Expand Up @@ -276,6 +276,7 @@ jobs:
yarn test packages/bcrypt
yarn test packages/crc32
yarn test packages/jieba
yarn test packages/jsonwebtoken
yarn test packages/xxhash

test-linux-x64-gnu-binding:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install
uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly-2023-01-11
toolchain: nightly-2023-03-01
components: rustfmt, clippy

- name: 'Install dependencies'
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ members = [
"./packages/crc32",
"./packages/deno-lint",
"./packages/jieba",
"./packages/jsonwebtoken",
"./packages/xxhash",
]

Expand Down
23 changes: 23 additions & 0 deletions packages/jsonwebtoken/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
authors = ["Francesco Benedetto"]
edition = "2021"
name = "node-rs-jsonwebtoken"
version = "0.1.0"

[lib]
crate-type = ["cdylib"]

[dependencies]
global_alloc = { path = "../../crates/alloc" }
jsonwebtoken = { version = "8" }
napi = { version = "2", default-features = false, features = [
"napi3",
"serde-json",
] }
napi-derive = { version = "2" }
serde = "1.0"
serde_json = "1.0"
rand = "0.8"

[build-dependencies]
napi-build = "2"
61 changes: 61 additions & 0 deletions packages/jsonwebtoken/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# `@node-rs/jsonwebtoken`

![](https://github.com/napi-rs/node-rs/workflows/CI/badge.svg)
![](https://img.shields.io/npm/dm/@node-rs/jsonwebtoken.svg?sanitize=true)

🚀 Fastest jsonwebtoken in Node.js

## Support matrix

| | node12 | node14 | node16 | node18 |
| ---------------- | ------ | ------ | ------ | ------ |
| Windows x64 | ✓ | ✓ | ✓ | ✓ |
| Windows x32 | ✓ | ✓ | ✓ | ✓ |
| Windows arm64 | ✓ | ✓ | ✓ | ✓ |
| macOS x64 | ✓ | ✓ | ✓ | ✓ |
| macOS arm64 | ✓ | ✓ | ✓ | ✓ |
| Linux x64 gnu | ✓ | ✓ | ✓ | ✓ |
| Linux x64 musl | ✓ | ✓ | ✓ | ✓ |
| Linux arm gnu | ✓ | ✓ | ✓ | ✓ |
| Linux arm64 gnu | ✓ | ✓ | ✓ | ✓ |
| Linux arm64 musl | ✓ | ✓ | ✓ | ✓ |
| Android arm64 | ✓ | ✓ | ✓ | ✓ |
| Android armv7 | ✓ | ✓ | ✓ | ✓ |
| FreeBSD x64 | ✓ | ✓ | ✓ | ✓ |

## Usage

See [tests](__tests__/jsonwebtoken.spec.ts) and [types](index.d.ts)

## Bench

```
Model Name: MacBook Pro
Model Identifier: MacBookPro18,2
Processor Name: Apple M1 Max
Processor Speed: 2.6 GHz
Number of Processors: 1
Total Number of Cores: 8
L2 Cache (per Core): 256 KB
L3 Cache: 12 MB
Hyper-Threading Technology: Disabled
Memory: 64 GB
```

```text
@node-rs/jsonwebtoken x 17,491 ops/sec ±0.39% (92 runs sampled)
node-jsonwebtoken x 6,899 ops/sec ±0.62% (88 runs sampled)
Async sign bench suite: Fastest is @node-rs/jsonwebtoken

@node-rs/jsonwebtoken x 17,393 ops/sec ±1.57% (87 runs sampled)
node-jsonwebtoken x 5,256 ops/sec ±0.74% (85 runs sampled)
Async verify bench suite: Fastest is @node-rs/jsonwebtoken

@node-rs/jsonwebtoken x 264,001 ops/sec ±0.08% (101 runs sampled)
node-jsonwebtoken x 71,785 ops/sec ±1.01% (97 runs sampled)
Sync sign bench suite: Fastest is @node-rs/jsonwebtoken

@node-rs/jsonwebtoken x 278,657 ops/sec ±0.08% (99 runs sampled)
node-jsonwebtoken x 54,462 ops/sec ±0.53% (100 runs sampled)
Sync verify bench suite: Fastest is @node-rs/jsonwebtoken
```
114 changes: 114 additions & 0 deletions packages/jsonwebtoken/__tests__/jsonwebtoken.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { promises as fs } from 'node:fs'
import { join } from 'node:path'

import test from 'ava'
import { decode as nodeJwtDecode } from 'jsonwebtoken'

import { Algorithm, sign, signSync, verifySync, verify } from '../index.js'

const getUtcTimestamp = () => Math.floor(new Date().getTime() / 1000)
const oneDayInSeconds = 86400

test('signSync and sign (async) should produce the same result', async (t) => {
const claims = {
data: {
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
pr: 33,
isM: true,
set: ['KL', 'TV', 'JI'],
nest: { id: 'poly' },
},
exp: getUtcTimestamp() + oneDayInSeconds,
}
const secretKey = 'secret'
const resSync = signSync(claims, secretKey)
const resAsync = await sign(claims, secretKey)

t.is(resSync, resAsync)
t.truthy(nodeJwtDecode(resAsync))
})

test('verify should return the decoded claims', async (t) => {
const data = {
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
pr: 33,
isM: true,
set: ['KL', 'TV', 'JI'],
nest: { id: 'poly' },
}
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
const secretKey = 'secret'
const headers = { algorithm: Algorithm.HS384 }

const token = await sign(claims, secretKey, headers)

const validation = { algorithms: [Algorithm.HS384] }
const decodedToken = await verify(token, secretKey, validation)

t.like(decodedToken, claims)
})

test('verify sync should return the decoded claims', async (t) => {
const data = {
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
pr: 33,
isM: true,
set: ['KL', 'TV', 'JI'],
nest: { id: 'poly' },
}
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
const publicKeyPem =
'-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzq7L/V03tpy3QTYOP51CT0fY2Sp5spejcja9brkEZoLYFcvLSeNnsXtPg/Sr7PwbykiXoY++xo7+6o2VfPnbiEFV8fNap+4tWDmxeZfPifmCEA58BFncnK8z5luxR+syeRuI/9IUHllGxsKoQAbFECZoNCR+I5H/ynqhm9rvk89iNsh5EGxknOq2GmMaKRZ3nHZtVuwUj3BlwgsmP28ZAofMN/xM8bugHS1nNNHmRh6Ubg0Od3r2FH0+3df86ZzJ013M/LG1189aGNPXDOH4guBh7TPficw9nUnhIghiEFrxhAvIOQjClbhFud931T+UqD5BsF/ZarJ1VkaUa3UjxwIDAQAB-----END PUBLIC KEY-----'
const privateKeyPem =
'-----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-----'
const headers = { algorithm: Algorithm.RS256 }

const token = await sign(claims, privateKeyPem, headers)

const validation = { algorithms: [Algorithm.RS256] }
const decodedToken = verifySync(token, publicKeyPem, validation)

t.like(decodedToken, claims)
})

test('verify should throw in case the token is expired', async (t) => {
const data = {
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
pr: 33,
isM: true,
set: ['KL', 'TV', 'JI'],
nest: { id: 'poly' },
}
const claims = { data, iat: getUtcTimestamp() - oneDayInSeconds * 2, exp: getUtcTimestamp() - oneDayInSeconds }
const secretKey = 'secret'
const headers = { algorithm: Algorithm.HS256 }

const token = await sign(claims, secretKey, headers)

t.throws(() => verifySync(token, secretKey))
await t.throwsAsync(() => verify(token, secretKey))
})

test('should allow to use a buffer as sign/verify key', async (t) => {
const [publicKeyPem, privateKeyPem] = await Promise.all([
fs.readFile(join(__dirname, 'public-key.pem')),
fs.readFile(join(__dirname, 'private-key.pem')),
])

const data = {
id: 'f81d4fae-7dec-11d0-a765-00a0c91e6bf6',
pr: 33,
isM: true,
set: ['KL', 'TV', 'JI'],
nest: { id: 'poly' },
}
const claims = { data, exp: getUtcTimestamp() + oneDayInSeconds }
const headers = { algorithm: Algorithm.PS256 }

const token = await sign(claims, privateKeyPem.toString(), headers)

const validation = { algorithms: [Algorithm.PS256] }
const decodedToken = await verify(token, publicKeyPem.toString(), validation)

t.like(decodedToken, claims)
})
27 changes: 27 additions & 0 deletions packages/jsonwebtoken/__tests__/private-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAowpInj/GxkrOtNVXG89JXmsglD495jzijM2xOEs4jIlKa+B2
QVIY296+GJUJtnvUboEIGBYwWZUVxBnvVpGYB+0nYTnZSn7A9+KpJ8Lpt+ui/Ibc
kfx6gw5CfSIeKUz5cIuaPxNIZByEUL4FI+6dQGVLc4eVWDCDRpR2RuOW1EcrbIEm
CKrhQgpWrmpc7v8JYj9bzcE63R0iWM8EM9fawbu6yJxFxqQ9vwZFZBYw7vsdj+Pa
9dNyhqTAtUr3c1+fZEDifYSEdGRJZld+2z7Pm2LEI7EFWRKC8LTHA4rC4N6U2B1k
wRWZ2gk7aXu1W4gu9qD79OIjcuhkuVx8OrJZMwIDAQABAoIBAQCgBcPorqgmj936
Vzq8LOPSLEs5tS2EAVZK5MiAfDPwm//TiegHjNChXSovbniuBzQlkbekDINAKbfH
Vb03tocFoJr6LpE7MNWtd2aXhBNpVXoPaT6seqa0YxaXQxlfaBGbiSnHpuFygRrN
NPROpDDrt4Aq0HSgrlzqtWSxh0fO6MqFrCZPfLelY+etDHfr+JkMG2qbEKYqv7ZF
e18Pvf97VwFebNTolWCKE8A+G8Ps7nUrsl5OiDYNGK42kPZNbM0uImgGgIzvVdYs
458WVwi4rA9A5vrzrANY9U+SMQg3A9Cr1DXMQBHb2MC0W/AxB5UWISEtNe99p7UH
uqtoQUwhAoGBAM1vOWgksEhoJJUAW3UWknAP/SOEqMxDYFTXx2y9q1F7g9Dhe9gC
+wqQQsXP0w2pGyBslFx8RWcP55uc/r1kcjbQbF0i2yWv8ZgVsloFh2WukBODNikp
CDyj8YHSHHi1bojwBeIv32sLt1awBKaUKuJhYgonpliJfvf9Edpm4HYPAoGBAMsr
uPHaWf1XjRjd4SpStwzIMBOtS49twriAZvqEBQcAHYhLldZJFpwLxYrwLk6eZK9E
fQgQDANCl4ez5bfgNR11QCjLiziX1K++e1sERbSGTIV/U6rHeDveaCU+rGG672MZ
P1mOl+trXtFcryfQ7tNyBfjD3E4jF00/yXs+OO6dAoGAMs/58QpyF9a6hahK9tEY
c2NhB3H+lldr8lBU4U6gm0zjs7yx9yH1mg1IlsjquQxEy2ZP4/hQ6kcC0HiqgYng
vjIbO4YtkkrMhQOI079eWAYvWMQxl0iw4t7iE2w24pxttK05p1KT/lQtiuGKpPEt
EkVoDH72JBwOLaSIz+52Qn0CgYA1B7qEViv69mk7vl5RP7nLukziNe9tBoc2xT0b
0m3FgAA1XRVFE1q1bFUpiLttheZd4RCJlDaueyk2IHyrW/hBMiOHAmnaYbcAEEX+
YcUX853xkmRyRRJa/hhM8GjqMXLeeO6SH6gDqMjc+MY3LE/KHQ71+Zl9Q6eHYEjk
xD7z+QKBgGC7MNG1EARfA4kgp7w+j5JBiCakZdXEzJFUzk994ZSBpY39bftecd/i
0OKuVPXX1qcN3en8p+qAPOriukBITkG4IVQvH+SJVEpV61E3+C+bs9jmp3RtBoiS
1/DWZxnwoqFYnzYLHEsKt4BwJ2fiSE0uUDGJdv+ZJhjSGt1R/fbJ
-----END RSA PRIVATE KEY-----
9 changes: 9 additions & 0 deletions packages/jsonwebtoken/__tests__/public-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAowpInj/GxkrOtNVXG89J
XmsglD495jzijM2xOEs4jIlKa+B2QVIY296+GJUJtnvUboEIGBYwWZUVxBnvVpGY
B+0nYTnZSn7A9+KpJ8Lpt+ui/Ibckfx6gw5CfSIeKUz5cIuaPxNIZByEUL4FI+6d
QGVLc4eVWDCDRpR2RuOW1EcrbIEmCKrhQgpWrmpc7v8JYj9bzcE63R0iWM8EM9fa
wbu6yJxFxqQ9vwZFZBYw7vsdj+Pa9dNyhqTAtUr3c1+fZEDifYSEdGRJZld+2z7P
m2LEI7EFWRKC8LTHA4rC4N6U2B1kwRWZ2gk7aXu1W4gu9qD79OIjcuhkuVx8OrJZ
MwIDAQAB
-----END PUBLIC KEY-----
Loading