Skip to content

nearform/fast-jwt

Repository files navigation

fast-jwt

Package Version CI

Fast JSON Web Token implementation.

Installation

Just run:

npm install fast-jwt

Usage

createSigner

Create a signer function by calling createSigner and providing one or more of the following options:

  • key: A string or a buffer containing the secret for HS* algorithms or the PEM encoded private key for RS*, PS*, ES* and EdDSA algorithms. If the key is a passphrase protected private key it must be an object (more details below). The key can also be a function accepting a Node style callback or a function returning a promise. This is the only mandatory option, which MUST NOT be provided if the token algorithm is none.

  • algorithm: The algorithm to use to sign the token. The default value is autodetected from the key, using RS256 for RSA private keys, HS256 for plain secrets and the corresponding ES or EdDSA algorithms for EC or Ed* private keys.

  • mutatePayload: If set to true, the original payload will be modified in place (via Object.assign) by the signing function. This is useful if you need a raw reference to the payload after claims have been applied to it but before it has been encoded into a token. Default is false.

  • expiresIn: Time span (in milliseconds or text describing time) after which the token expires, added as the exp claim in the payload as defined by the section 4.1.4 of RFC 7519. This will override any existing value in the claim.

    Eg: 60, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms"). For more info look into @lukeed/ms.

  • notBefore: Time span (in milliseconds or text describing time) before the token is active, added as the nbf claim in the payload as defined by the section 4.1.5 of RFC 7519. This will override any existing value in the claim.

    Eg: 60, "2 days", "10h", "7d". A numeric value is interpreted as a seconds count. If you use a string be sure you provide the time units (days, hours, etc), otherwise milliseconds unit is used by default ("120" is equal to "120ms"). For more info look into @lukeed/ms.

  • jti: The token unique identifier, added as the jti claim in the payload as defined by the section 4.1.7 of RFC 7519. This will override any existing value in the claim.

  • aud: The token audience, added as the aud claim in the payload as defined by the section 4.1.3 of RFC 7519. This claim identifies the recipients that the token is intended for. It must be a string or an array of strings. This will override any existing value in the claim.

  • iss: The token issuer, added as the iss claim in the payload as defined by the section 4.1.1 of RFC 7519. It must be a string. This will override any existing value in the claim.

  • sub: The token subject, added as the sub claim in the payload as defined by the section 4.1.2 of RFC 7519. It must be a string. This will override any existing value in the claim.

  • nonce: The token nonce, added as the nonce claim in the payload. The nonce value is used to associate a Client session with an ID Token. Note that this is a IANA JSON Web Token Claims Registry public claim registered by OpenID Connect (OIDC). It must be a string. This will override any existing value in the claim.

  • kid: The token key id, added as the kid claim in the header section (see section 4.1.4 of RFC 7515 and section 4.5 of RFC 7517). It must be a string.

  • header: Additional claims to add to the header section. This will override the typ and kid claims.

  • noTimestamp: If set to true, the iat claimΒ should not be added to the token. Default is false.

  • clockTimestamp: The timestamp in milliseconds (like the output of Date.now()) that should be used as the current time for all necessary time comparisons. Default is the system time.

The signer is a function which accepts a payload and returns the token.

The payload must be an object.

If the key option is a function, the signer will also accept a Node style callback and will return a promise, supporting therefore both callback and async/await styles.

If the key is a passphrase protected private key, the algorithm option must be provided and must be either a RS* or ES* encoded key and the key option must be an object with the following structure:

{
  key: '<YOUR_RSA_ENCRYPTED_PRIVATE_KEY>',
  passphrase: '<PASSPHRASE_THAT_WAS_USED_TO_ENCRYPT_THE_PRIVATE_KEY>'
}

Example

const { createSigner } = require('fast-jwt')

// Sync style
const signSync = createSigner({ key: 'secret' })
const token = signSync({ a: 1, b: 2, c: 3 })
// => eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9.mIcxteEVjbh2MnKQ3EQlojZojGSyA_guqRBYHQURcfnCSSBTT2OShF8lo9_ogjAv-5oECgmCur_cDWB7x3X53g

// Callback style
const signWithCallback = createSigner({ key: (callback) => callback(null, 'secret') })

signWithCallback({ a: 1, b: 2, c: 3 }, (err, token) => {
  // token === eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9.mIcxteEVjbh2MnKQ3EQlojZojGSyA_guqRBYHQURcfnCSSBTT2OShF8lo9_ogjAv-5oECgmCur_cDWB7x3X53g
})

// Promise style - Note that the key function style and the signer function style are unrelated
async function test() {
  const signWithPromise = createSigner({ key: async () => 'secret' })

  const token = await signWithPromise({ a: 1, b: 2, c: 3 })
  // => eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9.mIcxteEVjbh2MnKQ3EQlojZojGSyA_guqRBYHQURcfnCSSBTT2OShF8lo9_ogjAv-5oECgmCur_cDWB7x3X53g
}

// Using password protected private key - in this case you MUST provide the algorithm as well
const signSync = createSigner({
  algorithm: '<ANY_RS*_OR_ES*_ALGORITHM>',
  key: {
    key: '<YOUR_RSA_ENCRYPTED_PRIVATE_KEY>',
    passphrase: '<PASSPHRASE_THAT_WAS_USED_TO_ENCRYPT_THE_PRIVATE_KEY>'
  })
const token = signSync({ a: 1, b: 2, c: 3 })

createDecoder

Create a decoder function by calling createDecoder and providing one or more of the following options:

  • complete: Return an object with the decoded header, payload, signature and input (the token part before the signature), instead of just the content of the payload. Default is false.

  • checkTyp: When validating the decoded header, setting this option forces the check of the typ property against this value. Example: checkTyp: 'JWT'. Default is undefined.

The decoder is a function which accepts a token (as Buffer or string) and returns the payload or the sections of the token.

Examples

const { createDecoder } = require('fast-jwt')
const token =
  'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9.mIcxteEVjbh2MnKQ3EQlojZojGSyA_guqRBYHQURcfnCSSBTT2OShF8lo9_ogjAv-5oECgmCur_cDWB7x3X53g'

// Standard decoder
const decode = createDecoder()
const payload = decode(token)
// => { a: 1, b: 2, c: 3, iat: 1579521212 }

// Complete decoder
const decodeComplete = createDecoder({ complete: true })
const sections = decodeComplete(token)
/* => 
  { 
    header: { alg: 'HS512', typ: 'JWT' }, 
    payload: { a: 1, b: 2, c: 3, iat: 1579521212 },
    signature: 'mIcxteEVjbh2MnKQ3EQlojZojGSyA/guqRBYHQURcfnCSSBTT2OShF8lo9/ogjAv+5oECgmCur/cDWB7x3X53g==',
    input: 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9' 
  }
*/

createVerifier

Create a verifier function by calling createVerifier and providing one or more of the following options:

  • key: A string or a buffer containing the secret for HS* algorithms or the PEM encoded public key for RS*, PS*, ES* and EdDSA algorithms. The key can also be a function accepting a Node style callback or a function returning a promise. This is the only mandatory option, which MUST NOT be provided if the token algorithm is none.

  • algorithms: List of strings with the names of the allowed algorithms. By default, all algorithms are accepted.

  • complete: Return an object with the decoded header, payload, signature and input (the token part before the signature), instead of just the content of the payload. Default is false.

  • cache: A positive number specifying the size of the verified tokens cache (using LRU strategy). Setting to true is equivalent to provide the size 1000. When enabled, as you can see in the benchmarks section below, performances dramatically improve. By default the cache is disabled.

  • cacheTTL: The maximum time to live of a cache entry (in milliseconds). If the token has a earlier expiration or the verifier has a shorter maxAge, the earlier takes precedence. The default is 600000, which is 10 minutes.

  • errorCacheTTL: A number or function function (tokenError) => number that represents the maximum time to live of a cache error entry (in milliseconds). Example: the key function fails or does not return a secret or public key. By default errors are not cached, the errorCacheTTL default value is -1.

  • allowedJti: A string, a regular expression, an array of strings or an array of regular expressions containing allowed values for the id claim (jti). By default, all values are accepted.

  • allowedAud: A string, a regular expression, an array of strings or an array of regular expressions containing allowed values for the audience claim (aud). By default, all values are accepted.

  • allowedIss: A string, a regular expression, an array of strings or an array of regular expressions containing allowed values for the issuer claim (iss). By default, all values are accepted.

  • allowedSub: A string, a regular expression, an array of strings or an array of regular expressions containing allowed values for the subject claim (sub). By default, all values are accepted.

  • allowedNonce: A string, a regular expression, an array of strings or an array of regular expressions containing allowed values for the nonce claim (nonce). By default, all values are accepted.

  • requiredClaims: An array of strings containing which claims should exist in the token. By default, no claim is marked as required.

  • ignoreExpiration: Do not validate the expiration of the token. Default is false.

  • ignoreNotBefore: Do not validate the activation of the token. Default is false.

  • maxAge: The maximum allowed age (in milliseconds) for tokens to still be valid. By default this is not checked.

  • clockTimestamp: The timestamp in milliseconds (like the output of Date.now()) that should be used as the current time for all necessary time comparisons. Default is the system time.

  • clockTolerance: Timespan in milliseconds is the tolerance to apply to the current timestamp when performing time comparisons. Default is 0.

  • cacheKeyBuilder: The function that will be used to create the cache's key for each token. To mitigate the risk of leaking sensitive information and generate collisions, a hashing function is used by default.

The verifier is a function which accepts a token (as Buffer or string) and returns the payload or the sections of the token.

If the key option is a function, the signer will also accept a Node style callback and will return a promise, supporting therefore both callback and async/await styles.

Examples

const { createVerifier, TOKEN_ERROR_CODES } = require('fast-jwt')
const token =
  'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9.mIcxteEVjbh2MnKQ3EQlojZojGSyA_guqRBYHQURcfnCSSBTT2OShF8lo9_ogjAv-5oECgmCur_cDWB7x3X53g'

// Sync style
const verifySync = createVerifier({ key: 'secret' })
const payload = verifySync(token)
// => { a: 1, b: 2, c: 3, iat: 1579521212 }

// Callback style with complete return
const verifyWithCallback = createVerifier({ key: callback => callback(null, 'secret'), complete: true })

verifyWithCallback(token, (err, sections) => {
  /*
  sections === {
    header: { alg: 'HS512', typ: 'JWT' },
    payload: { a: 1, b: 2, c: 3, iat: 1579521212 },
    signature: 'mIcxteEVjbh2MnKQ3EQlojZojGSyA/guqRBYHQURcfnCSSBTT2OShF8lo9/ogjAv+5oECgmCur/cDWB7x3X53g==',
    input: 'eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhIjoxLCJiIjoyLCJjIjozLCJpYXQiOjE1Nzk1MjEyMTJ9'
  }
*/
})

// Promise style - Note that the key function style and the verifier function style are unrelated
async function test() {
  const verifyWithPromise = createVerifier({ key: async () => 'secret' })

  const payload = await verifyWithPromise(token)
  // => { a: 1, b: 2, c: 3, iat: 1579521212 }
}

// custom errorCacheTTL verifier
const verifier = createVerifier({
  key: 'secret',
  cache: true,
  errorCacheTTL: tokenError => {
    // customize the ttl based on the error code
    if (tokenError.code === TOKEN_ERROR_CODES.invalidKey) {
      return 1000
    }
    return 2000
  }
})

Creating a certificate

Many different algorithms are supported and appropriate certificates can be created through various external applications. Here is one example to create RSA certificates with openssl.

PRIVATE_PEM="./jwt-private.pem"
PUBLIC_PEM="./jwt-public.pem"

ssh-keygen -t rsa -b 2048 -m PEM -f "$PRIVATE_PEM" -q -N ""
openssl rsa -in "$PRIVATE_PEM" -pubout -outform PEM -out "$PUBLIC_PEM" 2>/dev/null
rm "$PRIVATE_PEM.pub"

Algorithms supported

This is the lisf of currently supported algorithms:

Name Description
none Empty algorithm - The token signature section will be empty
HS256 HMAC using SHA-256 hash algorithm
HS384 HMAC using SHA-384 hash algorithm
HS512 HMAC using SHA-512 hash algorithm
ES256 ECDSA using P-256 curve and SHA-256 hash algorithm
ES384 ECDSA using P-384 curve and SHA-384 hash algorithm
ES512 ECDSA using P-521 curve and SHA-512 hash algorithm
RS256 RSASSA-PKCS1-v1_5 using SHA-256 hash algorithm
RS384 RSASSA-PKCS1-v1_5 using SHA-384 hash algorithm
RS512 RSASSA-PKCS1-v1_5 using SHA-512 hash algorithm
PS256 RSASSA-PSS using SHA-256 hash algorithm
PS384 RSASSA-PSS using SHA-384 hash algorithm
PS512 RSASSA-PSS using SHA-512 hash algorithm
EdDSA EdDSA tokens using Ed25519 or Ed448 keys, only supported on Node.js 12+

Caching

fast-jwt supports caching of verified tokens.

The cache layer, powered by mnemonist, is a LRU cache which dimension is controlled by the user, as described in the options list.

When caching is enabled, verified tokens are always stored in cache. If the verification fails once, the error is cached as well for the time set by errorCacheTTL and the operation is not retried.

For verified tokens, caching considers the time sensitive claims of the token (iat, nbf and exp) and make sure the verification is retried after a token becomes valid or after a token becomes expired.

Performances improvements varies by uses cases and by the type of the operation performed and the algorithm used.

The default cacheKeyBuilder is a function that hashes the token. This provides a good level of protection against sensitive information leaks, but it also has a significant performance impact (almost 10x slower, as it's a CPU bound operation). If you are using caching and you are not concerned about potential information leaks you can use the identity function as cacheKeyBuilder to improve them.

For a detailed discussion about it, take a look at this issue.

Note: Errors are not cached by default, to change this behaviour use the errorCacheTTL option.

Token Error Codes

Error codes exported by TOKEN_ERROR_CODES.

JWKS

JWKS is supported via get-jwks. Check out the documentation for integration examples.

Benchmarks

Signing

╔══════════════════════════════╀═════════╀═════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                 β”‚ Samples β”‚          Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ HS512 - fast-jwt (async)     β”‚   10000 β”‚ 55766.29 op/sec β”‚  Β± 2.85 % β”‚                         β•‘
β•‘ HS512 - jsonwebtoken (async) β”‚   10000 β”‚ 68764.89 op/sec β”‚  Β± 1.25 % β”‚ + 23.31 %               β•‘
β•‘ HS512 - jsonwebtoken (sync)  β”‚   10000 β”‚ 70191.14 op/sec β”‚  Β± 1.84 % β”‚ + 25.87 %               β•‘
β•‘ HS512 - jose (sync)          β”‚   10000 β”‚ 72844.84 op/sec β”‚  Β± 1.72 % β”‚ + 30.63 %               β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                 β”‚ Samples β”‚          Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ HS512 - fast-jwt (sync)      β”‚   10000 β”‚ 97602.16 op/sec β”‚  Β± 1.83 % β”‚ + 75.02 %               β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔══════════════════════════════╀═════════╀═══════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ ES512 - fast-jwt (async)     β”‚    1000 β”‚ 419.29 op/sec β”‚  Β± 0.34 % β”‚                         β•‘
β•‘ ES512 - jsonwebtoken (async) β”‚    1000 β”‚ 440.53 op/sec β”‚  Β± 0.26 % β”‚ + 5.07 %                β•‘
β•‘ ES512 - jsonwebtoken (sync)  β”‚    1000 β”‚ 445.91 op/sec β”‚  Β± 0.16 % β”‚ + 6.35 %                β•‘
β•‘ ES512 - jose (sync)          β”‚    1000 β”‚ 452.01 op/sec β”‚  Β± 0.20 % β”‚ + 7.80 %                β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ ES512 - fast-jwt (sync)      β”‚    1000 β”‚ 467.54 op/sec β”‚  Β± 0.15 % β”‚ + 11.51 %               β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔══════════════════════════════╀═════════╀═══════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - fast-jwt (async)     β”‚    1000 β”‚ 196.13 op/sec β”‚  Β± 0.28 % β”‚                         β•‘
β•‘ RS512 - jsonwebtoken (async) β”‚    1000 β”‚ 200.15 op/sec β”‚  Β± 0.23 % β”‚ + 2.05 %                β•‘
β•‘ RS512 - jsonwebtoken (sync)  β”‚    1000 β”‚ 203.72 op/sec β”‚  Β± 0.18 % β”‚ + 3.87 %                β•‘
β•‘ RS512 - jose (sync)          β”‚    1000 β”‚ 245.89 op/sec β”‚  Β± 0.39 % β”‚ + 25.37 %               β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - fast-jwt (sync)      β”‚    1000 β”‚ 273.31 op/sec β”‚  Β± 0.27 % β”‚ + 39.36 %               β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔══════════════════════════════╀═════════╀═══════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ PS512 - jsonwebtoken (sync)  β”‚    1000 β”‚ 194.00 op/sec β”‚  Β± 0.27 % β”‚                         β•‘
β•‘ PS512 - jsonwebtoken (async) β”‚    1000 β”‚ 202.08 op/sec β”‚  Β± 0.21 % β”‚ + 4.17 %                β•‘
β•‘ PS512 - fast-jwt (async)     β”‚    1000 β”‚ 203.36 op/sec β”‚  Β± 0.19 % β”‚ + 4.82 %                β•‘
β•‘ PS512 - jose (sync)          β”‚    1000 β”‚ 266.54 op/sec β”‚  Β± 0.29 % β”‚ + 37.39 %               β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                 β”‚ Samples β”‚        Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ PS512 - fast-jwt (sync)      β”‚    1000 β”‚ 272.11 op/sec β”‚  Β± 0.24 % β”‚ + 40.26 %               β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔══════════════════════════╀═════════╀═════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests             β”‚ Samples β”‚          Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ EdDSA - fast-jwt (async) β”‚    1000 β”‚  8301.50 op/sec β”‚  Β± 0.70 % β”‚                         β•‘
β•‘ EdDSA - jose (sync)      β”‚    1500 β”‚ 16561.83 op/sec β”‚  Β± 0.88 % β”‚ + 99.50 %               β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test             β”‚ Samples β”‚          Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ EdDSA - fast-jwt (sync)  β”‚    3000 β”‚ 17514.99 op/sec β”‚  Β± 0.94 % β”‚ + 110.99 %              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Decoding

╔═════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                    β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - jsonwebtoken - complete β”‚   10000 β”‚ 126201.23 op/sec β”‚  Β± 2.84 % β”‚                         β•‘
β•‘ RS512 - jsonwebtoken            β”‚   10000 β”‚ 143571.03 op/sec β”‚  Β± 1.82 % β”‚ + 13.76 %               β•‘
β•‘ RS512 - jose - complete         β”‚   10000 β”‚ 252738.76 op/sec β”‚  Β± 5.62 % β”‚ + 100.27 %              β•‘
β•‘ RS512 - fast-jwt                β”‚   10000 β”‚ 254921.59 op/sec β”‚  Β± 3.39 % β”‚ + 102.00 %              β•‘
β•‘ RS512 - jose                    β”‚   10000 β”‚ 266197.51 op/sec β”‚  Β± 4.02 % β”‚ + 110.93 %              β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                    β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - fast-jwt - complete     β”‚   10000 β”‚ 284719.82 op/sec β”‚  Β± 3.39 % β”‚ + 125.61 %              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Note that for decoding the algorithm is irrelevant, so only one was measured.

Verifying

╔═════════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ HS512 - jsonwebtoken (sync)         β”‚   10000 β”‚  49275.12 op/sec β”‚  Β± 1.41 % β”‚                         β•‘
β•‘ HS512 - fast-jwt (async)            β”‚   10000 β”‚  51353.81 op/sec β”‚  Β± 2.98 % β”‚ + 4.22 %                β•‘
β•‘ HS512 - jsonwebtoken (async)        β”‚   10000 β”‚  51610.98 op/sec β”‚  Β± 1.51 % β”‚ + 4.74 %                β•‘
β•‘ HS512 - jose (sync)                 β”‚   10000 β”‚  64280.92 op/sec β”‚  Β± 1.73 % β”‚ + 30.45 %               β•‘
β•‘ HS512 - fast-jwt (sync)             β”‚   10000 β”‚  75067.57 op/sec β”‚  Β± 2.40 % β”‚ + 52.34 %               β•‘
β•‘ HS512 - fast-jwt (async with cache) β”‚   10000 β”‚ 175013.21 op/sec β”‚  Β± 4.42 % β”‚ + 255.18 %              β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ HS512 - fast-jwt (sync with cache)  β”‚   10000 β”‚ 207199.64 op/sec β”‚  Β± 3.15 % β”‚ + 320.50 %              β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔═════════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ ES512 - fast-jwt (async)            β”‚    1000 β”‚    561.01 op/sec β”‚  Β± 0.44 % β”‚                         β•‘
β•‘ ES512 - jsonwebtoken (sync)         β”‚    1000 β”‚    573.52 op/sec β”‚  Β± 0.27 % β”‚ + 2.23 %                β•‘
β•‘ ES512 - jsonwebtoken (async)        β”‚    1000 β”‚    573.74 op/sec β”‚  Β± 0.26 % β”‚ + 2.27 %                β•‘
β•‘ ES512 - fast-jwt (sync)             β”‚    1000 β”‚    597.68 op/sec β”‚  Β± 0.30 % β”‚ + 6.54 %                β•‘
β•‘ ES512 - jose (sync)                 β”‚    1000 β”‚    604.42 op/sec β”‚  Β± 0.27 % β”‚ + 7.74 %                β•‘
β•‘ ES512 - fast-jwt (async with cache) β”‚   10000 β”‚ 189999.48 op/sec β”‚  Β± 4.49 % β”‚ + 33767.60 %            β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ ES512 - fast-jwt (sync with cache)  β”‚   10000 β”‚ 192353.61 op/sec β”‚  Β± 4.79 % β”‚ + 34187.22 %            β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔═════════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - jsonwebtoken (async)        β”‚    1500 β”‚   7551.10 op/sec β”‚  Β± 0.92 % β”‚                         β•‘
β•‘ RS512 - jsonwebtoken (sync)         β”‚    4500 β”‚   7750.46 op/sec β”‚  Β± 0.96 % β”‚ + 2.64 %                β•‘
β•‘ RS512 - fast-jwt (async)            β”‚    1000 β”‚   8413.41 op/sec β”‚  Β± 0.99 % β”‚ + 11.42 %               β•‘
β•‘ RS512 - jose (sync)                 β”‚    4500 β”‚  12382.58 op/sec β”‚  Β± 0.94 % β”‚ + 63.98 %               β•‘
β•‘ RS512 - fast-jwt (sync)             β”‚    4500 β”‚  12665.45 op/sec β”‚  Β± 0.90 % β”‚ + 67.73 %               β•‘
β•‘ RS512 - fast-jwt (sync with cache)  β”‚   10000 β”‚ 145107.65 op/sec β”‚  Β± 7.54 % β”‚ + 1821.68 %             β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ RS512 - fast-jwt (async with cache) β”‚   10000 β”‚ 158780.83 op/sec β”‚  Β± 3.90 % β”‚ + 2002.75 %             β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔═════════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ PS512 - jsonwebtoken (async)        β”‚    2500 β”‚   7240.21 op/sec β”‚  Β± 0.89 % β”‚                         β•‘
β•‘ PS512 - jsonwebtoken (sync)         β”‚    2000 β”‚   7449.38 op/sec β”‚  Β± 0.91 % β”‚ + 2.89 %                β•‘
β•‘ PS512 - fast-jwt (async)            β”‚    1500 β”‚   8301.99 op/sec β”‚  Β± 0.81 % β”‚ + 14.67 %               β•‘
β•‘ PS512 - jose (sync)                 β”‚    4000 β”‚  11944.57 op/sec β”‚  Β± 0.99 % β”‚ + 64.98 %               β•‘
β•‘ PS512 - fast-jwt (sync)             β”‚    1000 β”‚  12881.96 op/sec β”‚  Β± 0.76 % β”‚ + 77.92 %               β•‘
β•‘ PS512 - fast-jwt (async with cache) β”‚   10000 β”‚ 155603.59 op/sec β”‚  Β± 4.27 % β”‚ + 2049.16 %             β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ PS512 - fast-jwt (sync with cache)  β”‚   10000 β”‚ 172097.91 op/sec β”‚  Β± 4.58 % β”‚ + 2276.97 %             β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

╔═════════════════════════════════════╀═════════╀══════════════════╀═══════════╀═════════════════════════╗
β•‘ Slower tests                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ EdDSA - fast-jwt (async)            β”‚    1000 β”‚   6370.58 op/sec β”‚  Β± 0.59 % β”‚                         β•‘
β•‘ EdDSA - jose (sync)                 β”‚    1000 β”‚   6538.90 op/sec β”‚  Β± 0.83 % β”‚ + 2.64 %                β•‘
β•‘ EdDSA - fast-jwt (sync)             β”‚    1000 β”‚   7078.93 op/sec β”‚  Β± 0.87 % β”‚ + 11.12 %               β•‘
β•‘ EdDSA - fast-jwt (async with cache) β”‚   10000 β”‚ 177457.09 op/sec β”‚  Β± 5.36 % β”‚ + 2685.57 %             β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ Fastest test                        β”‚ Samples β”‚           Result β”‚ Tolerance β”‚ Difference with slowest β•‘
β•Ÿβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β•’
β•‘ EdDSA - fast-jwt (sync with cache)  β”‚   10000 β”‚ 202628.41 op/sec β”‚  Β± 3.12 % β”‚ + 3080.69 %             β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•§β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

Contributing

See CONTRIBUTING.md

License

Licensed under the Apache-2.0 license.