microcipcip / cookie-universal

Universal cookie plugin, perfect for SSR
525 stars 39 forks source link

Can't use this with axios interceptors #74

Closed marssantoso closed 4 years ago

marssantoso commented 4 years ago

So, I plan on using this module to set a cookie before each http request with axios, and having it removed once there is a response. Referring to nuxt's axios module docs, I should be able to do it with interceptors (https://axios.nuxtjs.org/helpers.html#interceptors).

So here's how I plan the code would look:

// plugins/axios.js
export default function ({ $axios, app }) {
  $axios.onRequest(() => {
    app.$cookies.set('uniqueCookie', 'some-obscure-random-code')
  })

  $axios.onResponse(() => {
    app.$cookies.remove('uniqueCookie')
  })
}

Pretty simple, right? And I expected that to work. Only not really.

Here's how it doesn't work. On the first request, the cookie isn't being set (or removed because it wasn't set in the first place). Only after the first response is returned, the cookie is being set. On the second request, the cookie that gets sent to the backend is actually from the first request. And after it got the response, the old cookie is being replaced by the new one.

I can guess why is that, but let me know if this is the expected behavior or why, because it's pretty unexpected to me.

Btw I'm using the cookie-universal-nuxt module for ssr.

microcipcip commented 4 years ago

@marssantoso Are you returning the request after setting the cookie? Using axios library directly, it would be like this:

axios.interceptors.request.use(function (config) {
     app.$cookies.set('uniqueCookie', 'some-obscure-random-code')
     return config;
  });

In your case, it should be like this?

export default function ({ $axios, app }) {
  $axios.onRequest((config) => {
    app.$cookies.set('uniqueCookie', 'some-obscure-random-code')
   return config
  })

  $axios.onResponse((config) => {
    app.$cookies.remove('uniqueCookie')
    return config
  })
}
microcipcip commented 4 years ago

You can try to add console.log(document.cookie) after you set the cookie, if it is set and is there, then the issue is with axios, not with this library. To me, the axios library is so simple to use that a module is not necessary. You can create a plugin yourself:

import Axios from 'axios'

const apiUrl = `${process.env.API_URL}`
const options = {
  baseURL: apiUrl,
  validateStatus (status) {
    return status >= 200 && status < 500
  },
}

const ax = {
  create: (options, ctx) => {
    const axiosInstance = Axios.create(options)

    // credentials, to fix CORS
    axiosInstance.defaults.withCredentials = true

    // request interceptor
    axiosInstance.interceptors.request.use(req => {
      ctx.app.$cookies.set('uniqueCookie', 'some-obscure-random-code')
      return req
    })

    // response interceptor
    axiosInstance.interceptors.response.use(res => {
      ctx.app.$cookies.remove('uniqueCookie')
      return res
    })

    return axiosInstance
  }
}

export default (ctx, inject) => {
  inject('axios', ax.create(options, ctx))
}
marssantoso commented 4 years ago

Are you returning the request after setting the cookie?

I did try that as well, but it made no difference.

You can try to add console.log(document.cookie) after you set the cookie

Not sure about this. If this is run from the server side, does it have a document object? Anyway, will give it a try in an hour or so and keep you up to date.

To me, the axios library is so simple to use that a module is not necessary.

You are right, but that module made it even simpler haha. Plus, it supports proxy right out of the box and I happen to need that as well.

marssantoso commented 4 years ago

Here's a bunch of console.logs

console.log(document.cookie) after setting the cookie with app.$cookies.set

document is not defined

console.log(config.headers.common.cookie) before returning the config object in onRequest

Empty on the first request. Present on the next one

console.logs in a server middleware. I'm passing the timestamps in client side and printing the timestamps in the server side too for comparison.

First request:

client:  undefined
server:  2020-07-24T03:36:45.255Z

Second request after a couple of minutes since the first one

client:  "2020-07-24T03:36:45.251Z"
server:  2020-07-24T03:38:28.079Z

Third request right after the second one

client:  undefined
server:  2020-07-24T03:38:39.404Z

Fourth request right after the the third one

client:  "2020-07-24T03:38:39.398Z"
server:  2020-07-24T03:38:47.350Z
microcipcip commented 4 years ago

Ok I understand now. This library does not set cookies in the request because doesn't make much sense, it set them to the response when is server side. If you think about it, it makes sense, the request comes from the client, so you are manipulating the client request if you add it there.

This is a workaround:

  axiosInstance.interceptors.request.use(req => {
      req.headers.Cookie += `uniqueCookie=${someObscureRandomCode}`
      return req
    })

In my code I had:

if (process.server) {
  req.headers.Cookie....
} else {
  ctx.app.$cookies.set...
}
marssantoso commented 4 years ago

Thank you very much. Works great on both server and client side.

Closing this issue now. Cheers.