user2741831
user2741831

Reputation: 2408

cant use jsonwebtoken with latest version of react

TL:DR: replace jsonwebtoken with jwt-decode

I have had some trouble with upgrading my existing react app to the latest version. Every time I do I get the following errors

Can't resolve 'buffer' in '/home/user/projects/trash/tokentest/test/node_modules/buffer-equal-constant-time'

Can't resolve 'stream' in '/home/user/projects/trash/tokentest/test/node_modules/buffer-equal-constant-time'

Can't resolve 'util' in '/home/user/projects/trash/tokentest/test/node_modules/buffer-equal-constant-time'

Can't resolve 'crypto' in '/home/user/projects/trash/tokentest/test/node_modules/buffer-equal-constant-time'

I can get rid of all these except crypto by installing the buffer stream and util packages.
I tried installing crypto, crypto-browserify and crypto-js.

I did figure out I could make it work by removing jsonwebtoken from the project. But its not fully functional anymore after that, since its needed for user authentication.

For testing I created a completely fresh create-react-app project. It works out of the box. But as soon as I install and import jsonwebtoken, I get the exact same errors again. Meaning even on a completely clean project, jsonwebtoken cannot be used.

Is there a way to fix this? Because I would like to upgrade my project and use jsonwebtoken.

Upvotes: 7

Views: 15690

Answers (4)

Chandra Prakash
Chandra Prakash

Reputation: 21

this is the class I have created to mock as jwt.

import * as jose from "jose";
import { convertToMilliseconds } from "../Helper/convertToMillisecond";
const SECRET = process.env.REACT_APP_PUBLIC_KEY;
const JWT_EXPIRY = convertToMilliseconds(process.env.JWT_EXPIRY || "24h");

class Jwt {
  async generateToken(payload) {
    try {
      const secret = new TextEncoder().encode(SECRET);
      const jwt = await new jose.SignJWT(payload)
        .setProtectedHeader({ alg: "HS256" })
        .setExpirationTime(new Date().getTime() + parseInt(JWT_EXPIRY))
        .sign(secret);
      return jwt;
    } catch (error) {
      throw error;
    }
  }
  async generateTokenWithExpiry(payload, expiry) {
    try {
      const expiryInMilliseconds = convertToMilliseconds(expiry);
      const secret = new TextEncoder().encode(SECRET);
      const jwt = await new jose.SignJWT(payload)
        .setProtectedHeader({ alg: "HS256" })
        .setExpirationTime(
          new Date().getTime() + parseInt(expiryInMilliseconds)
        )
        .sign(secret);
      return jwt;
    } catch (error) {
      throw error;
    }
  }
  async generateTokenWithExpiryAndNewSecret(payload, expiry, newSecret) {
    try {
      const secret = new TextEncoder().encode(newSecret);
      const jwt = await new jose.SignJWT(payload)
        .setProtectedHeader({ alg: "HS256" })
        .setExpirationTime(new Date().getTime() + expiry)
        .sign(secret);
      return jwt;
    } catch (error) {
      throw error;
    }
  }
  async decodeToken(token) {
    try {
      const jwt = jose.decodeJwt(token);
      return jwt;
    } catch (error) {
      throw error;
    }
  }
  async verifyToken(token) {
    try {
      const secret = new TextEncoder().encode(SECRET);
      return await jose.jwtVerify(token, secret);
    } catch (error) {
      throw error;
    }
  }
}

export default new Jwt();

File ->../Helper/convertToMillisecond

export const convertToMilliseconds = (expiry) => {
  const expiryValue = parseInt(expiry.substring(0, expiry.length - 1));
  const expiryUnit = expiry.substring(expiry.length - 1);
  let expiryInMilliseconds = 0;
  switch (expiryUnit) {
    case "y":
      expiryInMilliseconds = expiryValue * 365 * 24 * 60 * 60 * 1000;
      break;
    case "w":
      expiryInMilliseconds = expiryValue * 7 * 24 * 60 * 60 * 1000;
      break;
    case "d":
      expiryInMilliseconds = expiryValue * 24 * 60 * 60 * 1000;
      break;
    case "h":
      expiryInMilliseconds = expiryValue * 60 * 60 * 1000;
      break;
    case "m":
      expiryInMilliseconds = expiryValue * 60 * 1000;
      break;
    case "s":
      expiryInMilliseconds = expiryValue * 1000;
      break;
    default:
      expiryInMilliseconds = expiryValue;
      break;
  }
  return expiryInMilliseconds;
};

Upvotes: 2

Gimhana
Gimhana

Reputation: 11

jsonwebtoken is a Node.js module, its previous use in a React application was based on a polyfill of standard Node.js modules. Most likely a) you are relying on slow js crypto that is no longer maintained and has no feature parity with Node's crypto and b) you have significantly increased your js package size. A React update could be a webpack update, which no longer uses the dubious encryption polyfill by default.

use jose instead of jsonwebtocken https://jwt.io/libraries?language=Node.js

Upvotes: 1

Smack Alpha
Smack Alpha

Reputation: 1980

Yes. Latest React Version is not supporting jsonwebtoken library. Use react-jwt library in react side to decode token

Library Link: https://www.npmjs.com/package/react-jwt

Upvotes: 1

user9775882
user9775882

Reputation:

jsonwebtoken is a Node.js module, your previous use of it in a react application relied upon a polyfill of the Node.js std modules. This most likely a) relied on slow js cryptography that isn't maintained anymore and lacks feature parity with Node's crypto and b) increased your js bundle size considerably. Updating react likely meant updating webpack which no longer defaults to using the questionable crypto polyfill.

It is better to rely on modules made for either browsers or "universal", you can discover such modules amongst the ones listed on jwt.io under "JavaScript". Out of the ones listed there, jose uses purely available Web APIs.

Upvotes: 13

Related Questions