fast-jwt

repository·master·Indexed 19 days ago

https://github.com/nearform/fast-jwt

A high-performance JSON Web Token (JWT) library for Node.js providing efficient signing, decoding, and verification. It supports various algorithms including HS*, RS*, PS*, ES*, and EdDSA, and features an optional LRU cache for verified tokens to significantly boost performance. The library includes utilities such as createSigner, createDecoder, and createVerifier to handle JWT operations synchronously or asynchronously.

Tokens
8.6K
Snippets
23
Records
27
Agent score
64%

What's inside fast-jwt

  1. Configure token verification caching

    master

    fast-jwt supports an LRU cache for verified tokens to improve performance.

    Key Configuration Options

    • errorCacheTTL: By default, errors are not cached. Setting this option enables caching of failed verifications for the specified duration.
    • cacheKeyBuilder: The default implementation hashes the token to prevent sensitive information leaks, but this is CPU-intensive (up to 10x slower). If security is not a concern, you can provide an identity function to improve performance.

    Important Warnings

    • Cache Collisions: If you provide a custom cacheKeyBuilder, ensure it produces unique keys for different tokens. Failure to do so can cause tokens to be mis-identified during verification.
    • Time-Sensitive Claims: The cache automatically considers iat, nbf, and exp claims to ensure verification is retried once a token becomes valid or expires.
  2. Compare fast-jwt performance for decoding (RS512)

    master

    When decoding tokens using RS512, fast-jwt's createDecoder() performs additional safety checks compared to libraries like jose. Specifically, fast-jwt:

    • Decodes and parses the header (required for checkTyp option and complete form).
    • Rejects any of the three segments containing characters outside the base64url alphabet to prevent non-canonical tokens from being accepted.

    While jose's decodeJwt is faster because it only reads the payload, fast-jwt provides a more robust decoding process by validating the entire token structure.

    RS512 - fast-jwt: 1.59 µs/iter
    RS512 - jose: 597.02 ns/iter
  3. Handle TokenErrors and error codes

    master

    When using the verifier, fast-jwt throws a TokenError for issues like expired tokens, invalid signatures, or malformed tokens. You can catch these errors and inspect the code property against the TOKEN_ERROR_CODES object to determine the specific cause.

    const { createVerifier, createSigner, TOKEN_ERROR_CODES } = require('fast-jwt')
    
    // Example 1: Handling an expired token
    const sign = createSigner({ key: 'secret', expiresIn: '1ms' })
    const verify = createVerifier({ key: 'secret' })
    
    const expiredToken = sign({ foo: 'bar' })
    
    setTimeout(() => {
      try {
        verify(expiredToken)
      } catch (err) {
        if (err.code === TOKEN_ERROR_CODES.expired) {
          console.error('Token verification failed because the token has expired.')
        } else {
          console.error('An unexpected error occurred:', err.message)
        }
      }
    }, 100);
    
    // Example 2: Handling an invalid signature
    const correctSigner = createSigner({ key: 'correct-secret' })
    const verifierWithWrongKey = createVerifier({ key: 'wrong-secret' })
    
    const tokenSignedWithCorrectKey = correctSigner({ data: 'payload' })
    
    try {
      verifierWithWrongKey(tokenSignedWithCorrectKey)
    } catch (err) {
      if (err.code === TOKEN_ERROR_CODES.invalidSignature) {
        console.error('Token verification failed due to an invalid signature.')
      } else {
        console.error('An unexpected error occurred:', err.message)
      }
    }
  4. Create RSA certificates with openssl

    master

    To use RSA-based algorithms (like RS256, RS384, RS512, PS256, PS384, or PS512), you need a private and public key pair. You can generate these using openssl and ssh-keygen.

    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"
  5. Performance benchmarks for RS512 algorithm

    master

    Benchmarks for the RS512 algorithm demonstrate the efficiency of fast-jwt when using caching.

    Key takeaways for RS512:

    • fast-jwt (sync with cache) is approximately 30x faster than fast-jwt (sync).
    • fast-jwt (sync with cache) is approximately 49x faster than fast-jwt (async).
    • fast-jwt (sync with cache) is approximately 41.59x faster than jose (async).
    RS512 - fast-jwt (sync with cache)      1.86 µs/iter   1.76 µs █                    
                                     (1.72 µs … 3.04 µs)   2.70 µs █                    
                                     (930.91  b …   1.04 kb) 940.57  b █▅▂▂▂              ▄ 
  6. Compare fast-jwt performance for verification (HS256)

    master

    For HS256 verification, fast-jwt offers several modes, including synchronous, asynchronous, and cached versions:

    • HS256 - fast-jwt (sync with cache): This is the fastest mode, significantly outperforming standard sync/async methods and other libraries like jsonwebtoken and jose.
    • HS256 - fast-jwt (sync): Faster than standard jsonwebtoken and jose implementations.
    • HS256 - fast-jwt (async): Provides asynchronous verification.

    Using the cache option can provide a massive performance boost (e.g., ~3.5x faster than jsonwebtoken (sync) and ~15x faster than jose (async)).

    HS256 - fast-jwt (sync with cache) summary:
    - 2.36x faster than HS256 - fast-jwt (sync)
    - 3.51x faster than HS256 - jsonwebtoken (sync)
    - 15.81x faster than HS256 - jose (async)
  7. Performance benchmarks for ES512 algorithm

    master

    Benchmarks for the ES512 algorithm show that fast-jwt (sync with cache) provides massive performance gains over standard implementations.

    Key takeaways for ES512:

    • fast-jwt (sync with cache) is over 830x faster than standard fast-jwt (sync) and other libraries like jsonwebtoken or jose in this category.
    ES512 - fast-jwt (sync with cache)    1.40 µs/iter   1.38 µs   █                   
                                  (1.21 µs … 73.67 µs)   2.42 µs  ██▅                 
                                  (  1.27 kb … 138.95 kb) 1.29 kb  ███▃▂▃▂▂            
  8. Performance benchmarks for HS512 algorithm

    master

    Benchmarks for the HS512 algorithm compare fast-jwt against other libraries like jose, jsonwebtoken, and @node-rs/jsonwebtoken.

    Key takeaways for HS512:

    • fast-jwt (sync with cache) is the fastest implementation, performing significantly better than other libraries.
    • fast-jwt (sync with cache) is approximately 13.87x faster than jose (async).
    • fast-jwt (sync with cache) is approximately 3.09x faster than jsonwebtoken (sync).
    HS512 - fast-jwt (sync with cache)                 1.31 µs/iter   1.22 µs █                    
                                     (1.16 µs … 3.18 µs)   3.13 µs █                    
                                     (803.68  b … 1.13 kb) 932.42  b ██  ▂ ▂             ▂
  9. Compare fast-jwt performance for signing (PS512, EdDSA)

    master

    The fast-jwt library provides both synchronous and asynchronous methods for signing tokens. Performance varies by algorithm:

    PS512 Signing

    fast-jwt (sync) performs competitively with other libraries like jsonwebtoken and @node-rs/jsonwebtoken.

    EdDSA Signing

    fast-jwt (sync) is highly efficient for EdDSA, performing significantly faster than jose (async) and comparable to @node-rs/jsonwebtoken (sync).

    Note: Benchmark results are based on an Apple M1 Pro with Node 24.14.1.

    ### EdDSA Performance Summary
    EdDSA - @node-rs/jsonwebtoken (sync) is ~1.06x faster than EdDSA - fast-jwt (sync).
    EdDSA - fast-jwt (sync) is ~1.75x faster than EdDSA - jose (async).
    EdDSA - fast-jwt (sync) is ~2.54x faster than EdDSA - fast-jwt (async).
  10. Create a signer with createSigner

    master

    Use createSigner to generate a function that signs a payload into a JWT. The resulting signer can be used in synchronous, callback, or Promise-based (async/await) styles depending on how the key is provided.

    Key Options

    • key (Mandatory, except for none algorithm): A string/buffer for HS* algorithms, or a PEM encoded private key for RS*, PS*, ES*, and EdDSA.
      • If the key is passphrase protected, provide an object: { key: '<PRIVATE_KEY>', passphrase: '<PASSPHRASE>' }.
      • If key is a function, the signer supports Node-style callbacks and Promises.
    • algorithm: The signing algorithm. Defaults to autodetection from the key.
    • expiresIn: Adds exp claim. Supports seconds (numeric) or strings via @lukeed/ms (e.g., '2 days', '10h').
    • notBefore: Adds nbf claim. Supports seconds or strings.
    • mutatePayload: If true, modifies the original payload object in place via Object.assign.
    • jti, aud, iss, sub, nonce, kid: Standard JWT claims to be added to the payload or header.
    • noTimestamp: If true, the iat claim is omitted.
    • clockTimestamp: Custom timestamp for time-based comparisons.
    const { createSigner } = require('fast-jwt')
    
    // Sync style
    const signSync = createSigner({ key: 'secret' })
    const token = signSync({ a: 1, b: 2, c: 3 })
    
    // Callback style
    const signWithCallback = createSigner({ key: (callback) => callback(null, 'secret') })
    signWithCallback({ a: 1, b: 2, c: 3 }, (err, token) => {
      // token is the signed JWT
    })
    
    // Promise style
    async function test() {
      const signWithPromise = createSigner({ key: async () => 'secret' })
      const token = await signWithPromise({ a: 1, b: 2, c: 3 })
    }
    
    // Using password protected private key
    const signWithPassphrase = createSigner({
      algorithm: 'RS256',
      key: {
        key: '<YOUR_RSA_ENCRYPTED_PRIVATE_KEY>',
        passphrase: '<PASSPHRASE_THAT_WAS_USED_TO_ENCRYPT_THE_PRIVATE_KEY>'
      }
    })
    const tokenPass = signWithPassphrase({ a: 1, b: 2, c: 3 })