2

I'm using the mswjs library for mocking APIs in a Nuxt 3 (3.0.0-rc.11) project; Everything is working fine until I try to import the 'jsonwebtoken' library to generate a JWT access token to mock the login API call.

Here is the login request handler:

//file path: @/mocks/auth.js
import {rest} from 'msw'
import { addBaseUrl, makeResponse } from './helpers'
import jwt from 'jsonwebtoken'

export default [
   rest.post(addBaseUrl('/api/auth/login/'), (req, res, ctx) => {
     const json = {
       access: jwt.sign(
         {test: 'test'},
         'secret',
         {expiresIn: 600}       )
     }
     return makeResponse({res, ctx, status: 200, delay: 1000, json})
  }),
]

The problem is that when I try to import jwt, it throws this error and everything stops working:

util.js:109 Uncaught TypeError: Cannot read properties of undefined (reading 'NODE_DEBUG')
    at node_modules/util/util.js (util.js:109:17)
    at __require (chunk-7NEK6ARH.js?v=bb8c835c:9:50)
    at node_modules/jws/lib/data-stream.js (data-stream.js:4:12)
    at __require (chunk-7NEK6ARH.js?v=bb8c835c:9:50)
    at node_modules/jws/lib/sign-stream.js (sign-stream.js:3:18)
    at __require (chunk-7NEK6ARH.js?v=bb8c835c:9:50)
    at node_modules/jws/index.js (index.js:2:18)
    at __require (chunk-7NEK6ARH.js?v=bb8c835c:9:50)
    at node_modules/jsonwebtoken/decode.js (decode.js:1:11)
    at __require (chunk-7NEK6ARH.js?v=bb8c835c:9:50)

The problem is caused by jws library which is a dependency of jsonwebtoken library. I have no idea about how to fix this issue, and I would really appreciate any help.

Here is my package.json file:

{
  "private": true,
  "scripts": {
    "start": "nuxt start",
    "build": "nuxt build",
    "dev": "nuxt dev --https --ssl-cert localhost.pem --ssl-key localhost-key.pem",
    "generate": "nuxt generate",
    "preview": "nuxt preview"
  },
  "devDependencies": {
    "@iconify/vue": "^4.0.0",
    "autoprefixer": "^10.4.8",
    "msw": "^0.47.3",
    "nuxt": "3.0.0-rc.11",
    "postcss": "^8.4.16",
    "tailwindcss": "^3.1.8"
  },
  "dependencies": {
    "@vue-leaflet/vue-leaflet": "^0.6.1",
    "@vuelidate/core": "^2.0.0-alpha.44",
    "@vuelidate/validators": "^2.0.0-alpha.31",
    "dayjs": "^1.11.5",
    "jsonwebtoken": "^8.5.1",
    "jwt-decode": "^3.1.2",
    "leaflet": "^1.9.1",
    "vue-google-charts": "^1.1.0",
    "vue3-carousel": "^0.1.46"
  },
  "packageManager": "yarn@3.2.3",
  "msw": {
    "workerDirectory": "public"
  }
}
mo3n
  • 1,522
  • 2
  • 10
  • 34

2 Answers2

2

Since jsonwebtoken seems to use node-jws, I think, that it will not work, because mswjs (mock service worker JS) runs in the browser and thus not on node.

I am currently looking for a solution to the same problem though as I'd like to keep using MSWjs for mocking my backend and at the same time creating valid JWTs (in the browser, because the mock service worker script runs in the browser, not in a backend).

I am going to try https://www.npmjs.com/package/jose in combination with MSWjs.

Edit: I have tried it out and it seems to work.

I have generated a publicKey for verifying and a privateKey for signing JWTs in my browser.ts like so:

export const { publicKey, privateKey } = await jose.generateKeyPair('ES256')

And used in my auth-handlers.ts, which I included in the handlers.ts like so:

import { privateKey } from './browser'
...
async function createJwt(foundUser: UserData | undefined): Promise<string> {
    return new jose.SignJWT({ 'urn:example:claim': true })
        .setProtectedHeader({ alg: 'ES256' })
        .setIssuedAt()
        .setIssuer('urn:example:issuer')
        .setAudience('urn:example:audience')
        .setExpirationTime('2h')
        .sign(privateKey)
}

It's important to keep the publicKey available for all of your handlers, because you'll use it to verify the JWT you get in the request header and perhaps use claims if need be.

Furthermore I avoided redirecting to a login screen and instead sent the {email: ..., password: ...} body as a POST request to /auth/login in case no cookie was found when processing a supposedly protected endpoint.

In reality you would redirect the user to a login form and - once completed - redirect the user back to the original URL from what I understand.

Finally I used jose to generate and sign the JWT and appended the result to the header of the auth-handler response using ctx.cookie('Authorization', 'Bearer' + jwt). Since it redirected back to the URL that it was called from (/api/user/login -> /api/auth/login -> cookie appeneded to response -> /api/user/login), I was able to process the login and return the Authorization: Bearer ... header, which I then could use for my next call.

I still need to figure out if I should use refresh tokens and how to do so, but I hope this helps you getting started.

Igor
  • 1,582
  • 6
  • 19
  • 49
1

I also faced this problem with application based on Nuxt 3 + Vite. Then I did some research and found a plugin that solved the problem

https://www.npmjs.com/package/vite-plugin-node-polyfills

import { nodePolyfills } from 'vite-plugin-node-polyfills'

// vite.config or vite section of nuxt.config
plugins: [
    nodePolyfills({
      protocolImports: true,
    }),
],
nzuyanov
  • 11
  • 1
  • In which file did you add this configuration? – Babas Dec 24 '22 at 15:17
  • I am trying to use the kafka-node package and in the browser I get the following error: vue-router.mjs:3451 TypeError: Cannot read properties of undefined (reading 'NODE_DEBUG') at node_modules/util/util.js (util.js:109:17) (highLevelProducer.js:3:12) at __require2 (chunk-7FP5O474.js?v=d03f7462:10:50) at node_modules/kafka-node/kafka.js (kafka.js:3:29) at kafka.js:18:58 I tried your solution, but didn't work. Do you have an idea how I could solve my issue? I though Nuxt being a ssr, it would not have a problem using the kafka packages. – Babas Dec 24 '22 at 15:24
  • You need to add this plugin at bundler level. Try to add this section at nuxt.config.js – nzuyanov Dec 26 '22 at 08:26