Giter VIP home page Giter VIP logo

Comments (38)

pi0 avatar pi0 commented on May 26, 2024 93

You are on a right way. The only change required for that plugin is that you can directly access app.$axios and call setToken on it. No need to import/export.

plugins/auth.js

export default ({store, app: { $axios }}) => {
  $axios.setToken(store.state.token)
}

from axios-module.

dpmccabe avatar dpmccabe commented on May 26, 2024 18

I spent a few hours today trying to figure this out and think I finally have a working solution for Nuxt 2.6.3. I have an store for the auth token and current user. YMMV with this code since I don't really know what I'm doing.

store/index.js

export const actions = {
  async nuxtServerInit ({ dispatch, commit }) {
    const token = this.$cookies.get('x-access-token')

    if (token) {
      try {
        await commit('auth/setToken', token)
        await dispatch('auth/fetchUser', token)
      } catch(e) {
        console.log(e)
        await dispatch('auth/logout')
      }
    }
  }
}

store/auth.js

export const state = () => ({
  user: null,
  token: null
})

export const mutations = {
  setUser(store, user) {
    store.user = user
  },

  resetUser(store) {
    store.user = null
  },

  setToken(store, token) {
    store.token = token
  },

  resetToken(store) {
    store.token = null
  }
}

export const actions = {
  async fetchUser({ commit }, token) {
    const { data } = await this.$axios.get('auth/me')
    commit('setUser', data)
  },

  async login({ commit }, id) {
    const { data } = await this.$axios.post('auth/login', { id: id })
    commit('setUser', data.user)
    commit('setToken', data.token)

    this.$cookies.set('x-access-token', data.token, {
      maxAge: 60 * 60
    })
  },

  async logout({ commit }) {
    commit('resetUser')
    commit('resetToken')
    this.$cookies.remove('x-access-token')
  }
}

plugins/axios.js

export default function ({ $axios, store, redirect }) {
  $axios.onRequest(config => {
    const token = store.state.auth.token
    if (token) config.headers.common['Authorization'] = `Bearer ${token}`

    console.log('Request:')
    console.log(config)
  })

  $axios.onResponse(response => {
    console.log('Response:')
    console.log(response)
  })

  $axios.onError(error => {
    const code = parseInt(error.response && error.response.status)

    if (code === 400) {
      redirect('/400')
    }
  })
}

middleware/auth_required.js

export default function ({ store, redirect }) {
  if (!store.state.auth.user) {
    return redirect('/login')
  }
}

The key to get this working is to ensure that I commit the token before the current user, since my middleware checks the latter, and to use the async in nextServerInit, which I think blocks the middleware from processing. I'm not positive on this detail, though, and would appreciate any feedback on this code.

from axios-module.

Tinostarn avatar Tinostarn commented on May 26, 2024 8

Same thing here, I do a this.$axios.setToken(token, 'Bearer') in nuxtServerInit() then the authorization header disappears on client side :(

from axios-module.

awronski avatar awronski commented on May 26, 2024 8

@m4tty-d I don't know what the recommended way but I am using this with success:

plugins/axios.js:

export default function ({ $axios, app, store }) {
  $axios.onRequest(config => {
    if (store.state.authToken) {
      config.headers.common['Authorization'] = store.state.authToken
    }
  })
}

from axios-module.

opgbaudouin avatar opgbaudouin commented on May 26, 2024 7

Hope this helps someone:

i have this:

/plugins/axios.js

import axios from 'axios';

function tokenHandler(config, store) {
    if (store.state.account.user && store.state.account.user.jwt) {
          config.headers.common['Authorization'] =  `Bearer ${store.state.account.user.jwt}`;
   }
    return config
}

export default (context) => {
    //store is the current store, both server and client side. and unique for nuxtServerInit
    let {store, app} = context;
    axios.interceptors.request.use(config => tokenHandler(config, store),
        function (error) {
            return Promise.reject(error);
        });
}

Of course add the 'plugin' to your nuxt.config.js

  plugins: [
        '@/plugins/vuetify',
        '@/plugins/axios'   <-- 
    ],

and inside the nuxtServerInit i set the needed store values (from cookies for example)

This is just a more complete example of some of the suggestions elsewhere..

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024 6

Interesting.

I do have a working version on my project right now. My approach was to set the token into the store during nuxtserverinit and then set the token again in the plugin for the client.

plugin

export default ({store, $axios}) => {
  $axios.setToken(store.state.token)
}

nuxtServerInit

nuxtServerInit ({commit}, {req, route, app, store}) {
    let cookies = new Cookies(req)
    let token = cookies.get('access-token')

    if (!token) return

    commit('setToken', token)
    app.$axios.setToken(token, 'Bearer')
  }

from axios-module.

TheDeveloperTom avatar TheDeveloperTom commented on May 26, 2024 6

@pi0 the same problem as @uptownhr has.

After $axios.setToken on the server side, the $axios instance loses token on the client side.

Could you explain, how to use the "magic" feature of the setting once?

from axios-module.

awronski avatar awronski commented on May 26, 2024 5

Hello @pi0

I am confused. Do I need to call $axios.setToken both on SSR and CSR?

I ask becouse now I have o code like this, plugin:

export default function ({ store, route, redirect, req, res, isClient, isServer, app: { $axios } }) {
  if (isServer) {
    const cookies = new Cookies(req, res)
    authToken = cookies.get(AUTH_TOKEN_KEY)
    if (authToken) {
      $axios.setToken(authToken)
    }
    [...]
  }

And the user is logged correctly. But later for CSR axios don't send Authorization header. Why is it?

When I add this to the plugin everything works fine:

  if (isClient && authToken) {
    $axios.setToken(authToken, 'Bearer')
  }

from axios-module.

pi0 avatar pi0 commented on May 26, 2024 4

@awronski As of rc2 we have a magical feature which allows doing that once. Just give us little more time as there are lots of works while preparing final release :)

from axios-module.

pi0 avatar pi0 commented on May 26, 2024 3

Axios module has a new home and finally, SSR is safe to use setToken and setHeader. The main cause was totally crazy! commit and this commit.

Upgrade to >= 3.1.3 is recommended for everyone!

yarn add @nuxtjs/axios@^3.1.3
# or
npm i @nuxtjs/axios@^3.1.3

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024 3

@awronski As of rc2 we have a magical feature which allows doing that once. Just give us little more time as there are lots of works while preparing final release :)

@pi0 i'm on rc8 and tried to use this magical feature of using setToken just once. Can you walk me through this? Currently i'm calling setToken from the nuxtServerInit app.$axios.setToken. Works went request is made from the server but client looses the token.

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024 2

from axios-module.

jslegers avatar jslegers commented on May 26, 2024 2

Is there any reason why $axios can't automatically take the token from eg. localStorage or app.$auth.$storage at runtime?

After days of debugging, I finally figured out how to...

  1. Get a new authorization token for Auth0 tokens that are almost exported, using the silent renewal process
  2. Update the token in my LocalStorage
  3. Update the token in my app.$auth.$storage state

I could not find ANY documentation on how to do this and kinda had to reverse-engineer the Nuxt Auth module & the Auth0.js library to get to this point.

I expected everything to finally work... only to realize that my app was STILL breaking.

Apparently, I also had to call app.$axios.setToken with the new token after my previous steps, so Axios would use the correct token.

How is anyone supposed to figure this out on their own?

from axios-module.

seekcx avatar seekcx commented on May 26, 2024 1

I tried to provide a solution that did not know if it was feasible.

from axios-module.

awronski avatar awronski commented on May 26, 2024 1

@pi one more question. It the global variables shared between concurrent requests fixed in the rc.3?

I have strange behavior.

Plugin code:

export default function ({ store, route, app: { $axios } }) {
  console.log( $axios.defaults.headers.common.Authorization )
   [...]
}
  1. I log user lets say with the Chrome browser.
  2. Than I access the page with the IE browser (without login)
  3. In the console log of the server I see Bearer of the first user.

In the CSR the tokens are set correctly.

from axios-module.

codeofsumit avatar codeofsumit commented on May 26, 2024 1

@opgbaudouin thanks a lot for this. Searched for two hours how to do this.

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

one solution for me was to create an axios plugin that exports $axios

let axios = null

export default ({store, $axios}) => {
  console.log('axios plugin init')
  $axios.setToken(store.state.token)
  axios = $axios
}

export {
  axios
}

now i'm able to import {axios} from '~plugins/axios' again.

Looks a bit weird for me so would love some feedback if this is a bad approach.

from axios-module.

pi0 avatar pi0 commented on May 26, 2024

@seekcx I've seen that PR. But directly integration of store into axios makes it optinized and maybe not everyone wants using vuex in their project. We need an enhancement in Nuxt core indeed that plugins need a way adding things to __NUXT__ variable and access it on client init. Meanwhile whats wrong with simply using per-project plugins like above to call setToken with store state?

from axios-module.

seekcx avatar seekcx commented on May 26, 2024

@pi0 Sorry, just did not pay attention to see you on a comment, really perfect solution to this problem.

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

@pi0 the export import was added in for a different reason. It was so I can import from my store so I don't have to pass in axios to all my actions.

I remember you mentioned there is an issue with this approach. Is there an issue or is this an OK approach?

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

Also what is the difference between accessing $axios from the context vs grabbing from ctx.app

from axios-module.

pi0 avatar pi0 commented on May 26, 2024

@uptownhr It works but unsafe for SSR. (Because global variables will be shared between concurrent requests and this is probably not what we want!). We can access token from context only ( $axios, app, store, etc )

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

from axios-module.

awronski avatar awronski commented on May 26, 2024

@uptownhr The problem is the axios module use defaults internally. Just check:
https://github.com/nuxt-community/modules/blob/master/modules/axios/plugin.js#L33

In the present implementation the module cannot be use on server. Therefore I cannot hydrate the store for server side rendering.

I am thinking about diffrent implementation. Where the token is not put in the axios defaults but retrived from the store from axios interceptors. This should be thread safe.

from axios-module.

awronski avatar awronski commented on May 26, 2024

@uptownhr Hi James!

thanks for sharing your code.

Your example is working but I think it is not correct.

You set token in the nuxtServerInit, so the token is set on the shared axios server instance.
The same for every client.

In my opinion this is potentially security problem.

from axios-module.

awronski avatar awronski commented on May 26, 2024

I did a quick fix inside the nuxtjs/axios/plugin.js:

function tokenHandler(config, store) {
  if (store.getters.authToken) {
    config.headers.common['Authorization'] = store.getters.authToken
  }
  return config
}

  //Token handling
  axios.interceptors.request.use(config => tokenHandler(config, store), errorHandler.bind(ctx))

Now I do not need to use setToken at all and it workds both server and client side.

I will try to fork a repo and make a PR.

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

Awesome solution. Where is the store passed in from and which store instance is this?

But man handling axios is pretty scary. I would have thought axios is segmented as long as you use axios provided in the context.

from axios-module.

uptownhr avatar uptownhr commented on May 26, 2024

from axios-module.

adrianoresende avatar adrianoresende commented on May 26, 2024

[2] After $axios.setToken on the server side, the $axios instance loses token on the client side.

Same thing here, what is solution?

@TheDeveloperTom @supertino7 @uptownhr @pi0

from axios-module.

vadim-givola avatar vadim-givola commented on May 26, 2024

same thing here, is there any beforeRequest method to set token before sending a request?

from axios-module.

opgbaudouin avatar opgbaudouin commented on May 26, 2024

IMPORTANT.

UPDATE:
This solution will APPEAR to work, but only makes the problem less visible. I.e. I simply do not know what Nuxt 'Does' on a new request - i suspect it will not 'reimport' anything - so by setting a module variable i just have another global that will not be correct.

I can see NO way expect passing the / using the $axios instance and passing it to the point where it is needed... (i.e. pages, vuex stores) but no longer 'clean' service files.

Sorry...

Note there is nothing wrong with nuxtjs/axios just the hack i use


I updated to Nuxt 2.4.0 and i saw some pretty strange behaviour from the code i posted before:.

import axios from "Axios"
export default (context) => {
    //store is the current store, both server and client side. and unique for nuxtServerInit
    let {store, app} = context;
    axios.interceptors.request.use(config => tokenHandler(config, store),
        function (error) {
            return Promise.reject(error);
        });
}

This causes the request handler to be ADDED for each time a full SSR page is rendered. This means you might 'phantom' tokens.

I do not know if this was the case in pre 2.4.0 - but i suspect it was also doing this.

I now use @nuxtjs/axios . I looked at the code and noticed the issues (about re-using the axios 'global').

However i didn't want to change my whole code (i.e. my JWT is stored in the store, so 'reactive').

so my code is now:
nuxt.config.js:

    modules: [
        '@nuxtjs/axios',
    ],
 plugins: [
       '@/plugins/axios',
    ],

And plugins/axios.js (so both server / client in the new plugin way for 2.4)

function tokenHandler(config, store) {
    if (store.state.account.user && store.state.account.user.jwt) {
       //we could just use $axios.setToken
      
        config.headers.common['Authorization'] = `Bearer ${store.state.account.user.jwt}`;
    }
    return config
}

//https://github.com/nuxt-community/axios-module/issues/28
import { setAxiosInstance } from '~/services/http'
export default ({ app, store }) => {
    let axiosInstance = app.$axios;
    //install the INSTANCE based handler. So each request this will be called
    axiosInstance.onRequest(config => tokenHandler(config, store));
  
    setAxiosInstance(axiosInstance);

}

then my services/http.js - this used to be a simple
'import axios from axios'.
is now:

//NOTE: NO import axios. 
let axiosInstance = null;

export function setAxiosInstance(instance) {
    axiosInstance = instance;
    axiosInstance.defaults.baseURL = API_ROOT;
}

//rest of code now uses axiosInstance where axios used to be.

Nothing else needed to change, and my code isn't clobbered by $axios. statements still .

from axios-module.

m4tty-d avatar m4tty-d commented on May 26, 2024

I'm pretty lost. :'( What is the recommended way of setting the token now? When I set it on the server side only, its lost in the client side.

from axios-module.

dpmccabe avatar dpmccabe commented on May 26, 2024

My app will eventually use a federated authentication system (passport-cas) that isn't and will probably never be one of the ones supported by auth-module.

from axios-module.

opgbaudouin avatar opgbaudouin commented on May 26, 2024

My app will eventually use a federated authentication system (passport-cas) that isn't and will probably never be one of the ones supported by auth-module.

Indeed https://auth.nuxtjs.org will be used by most but more traditional auth methods (enterprise) it doesn't do.

from axios-module.

garan82 avatar garan82 commented on May 26, 2024

Do we have a recommended way to pass authorization header by default? I used awronski example here #298 (comment) but on server side I dont see authorization header at all. log in/log out work with auth middleware, but when I initiate axios.get myself, authorization header is not attached.

from axios-module.

cswkim avatar cswkim commented on May 26, 2024

@m4tty-d I don't know what the recommended way but I am using this with success:

plugins/axios.js:

export default function ({ $axios, app, store }) {
  $axios.onRequest(config => {
    if (store.state.authToken) {
      config.headers.common['Authorization'] = store.state.authToken
    }
  })
}

Using nuxt v2.14.6.

Can setToken work with a privateRuntimeConfig setting? I'm trying to send a bearer authorization token that is stored in my .env and read into:

privateRuntimeConfig: {
  apiToken: process.env.API_TOKEN
}

I tried to test the interceptor plugin approach like so:

export default function ({ $config: { apiToken }, $axios }) {
  $axios.onRequest((config) => {
    console.log(apiToken)
  })
}

and in my console I see 2 logs: one underneath Nuxt SSR that shows the correct value and then undefined. I'm making a this.$axios.$get call in a page file created() method. Because of the undefined, I get an error returned from the 3rd party API saying the token is missing.

UPDATE
I didn't realize certain areas (asyncData, plugins, created() method, etc.) run twice, once on the server-side and another time client-side. So the first time they run the server has access to privateRuntimeConfig, but the second time the client does not. So when I try to set the bearer auth token in a plugin, it works when server-side, but then immediately gets set to undefined on the second run client-side. I'm not sure what to do with this information hah. Can I wrap the interceptor request in the plugin file with a check for process.server so it only runs on the server?

from axios-module.

farnabaz avatar farnabaz commented on May 26, 2024

Can I wrap the interceptor request in the plugin file with a check for process.server so it only runs on the server?

@cswkim
This solution does not works in your case. You need to set valid token in client side as well as server. Setting token on one of them does not affect axios instance on another.
In other words, you should use publicRuntimeConfig.

from axios-module.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.