nuxt-community / auth-module

Zero-boilerplate authentication support for Nuxt 2
https://auth.nuxtjs.org
MIT License
1.94k stars 925 forks source link

autoFetchUser option "false" behaviour #561

Closed RokasDie closed 4 years ago

RokasDie commented 4 years ago

Hello,

I would like to understand how autoFetchUser "false" option behavior:

I understand that if I set it to "false" it does not send a user request once you successfully login (my login requests return user id). However, after logging in this.$auth.loggedIn returns false (means that user is not logged in), until I refresh the webpage and user endpoint is requested again. So what I see that with this option turned off for the user to be logged in I need to refresh webpage to request user.

Is this wroking as intended or I have incorrect setup?

Login Form

<template>
  <form>
    <input v-model="login.username" placeholder="enter username" />
    <input v-model="login.password" placeholder="enter password" />
    <div>
      <button @click="userLogin" type="button">Submit</button>
    </div>
  </form>
</template>

<script>
export default {
  data() {
    return {
      login: { username: '', password: '' }
    }
  },
  methods: {
    async userLogin() {
      try {
        await this.$auth.loginWith('local', {
          data: this.login
        })
        console.log(this.$auth.loggedIn) 
        // Here it returns false even though my endpoint returns user id, for example: {user: 3}

        this.$router.push('/')
      } catch (err) {
        console.log(err)
      }
    }
  }
}
</script>

nuxt.config.js

auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: 'api/auth/login',
            method: 'post',
            propertyName: 'user'
          },
          user: { url: 'api/auth/user', method: 'get', propertyName: 'user' }
        },
        tokenRequired: false,
        tokenType: false,
        autoFetchUser: false
      }
    },
    redirect: { home: '/' }
  },

  /*
   ** Axios module configuration
   ** See https://axios.nuxtjs.org/options
   */
  axios: {
    proxy: true
  },
  proxy: { '/api/': 'http://localhost:4000/' },

Regards, Rokas

JoaoPedroAS51 commented 4 years ago

Hi @RokasDie! Yes, if you set it to false it doesn't send a user request once you successfully login. But this.$auth.loggedIn state is based on user data, which means that you must define the user data right after login. You can set it using this.$auth.setUser({ ... }).

You can do something like this:

this.$auth.loginWith('local', {
    data: this.login
}).then(response => {
    this.$auth.setUser(response.data.user)
})

Note: I took a look at the documentation and this.$auth.setUser({ ... }) wasn't documented. I'm gonna open a PR fixing this issue.

JoaoPedroAS51 commented 4 years ago

@RokasDie You can set login endpoint propertyName to false, because it's only used for token.

RokasDie commented 4 years ago

@JoaoPedroAS51 Thanks for swift response. I checked and the setUser and it works :)

Regarding the propertyName, your are suggesting to remove it only for login, and do a check if the response is empty to not to login user in case something is wrong?

Should propertyName be removed for user endpoint also?

Regards, Rokas

JoaoPedroAS51 commented 4 years ago

@RokasDie Yes, remove it only from login endpoint. User endpoint requires the propertyName because your user data is inside user in your response. And I think that set user value to false if undefined is enough :)

const user = response.data.user || false
this.$auth.setUser(user)
felipe-muner commented 4 years ago

Hello guys, amazing job here.

I have one more question about the behaviour of the fetch user, down below you can check my nuxt.config.js: I read and I know that autoFetchUser:false just cancel the request after login. Maybe it's my wrong understand.. I would like to know, what's the point to have a endpoint user if I get all this information inside endpoints login.

To stop get error on request because I even didn't have the route on my server, I just created one route to satisfy the user endpoint and then I return null.

  auth: {
    strategies: {
      local: {
        autoFetchUser: false,
        endpoints: {
          login: {
            url: "/login",
            method: "post",
            propertyName: "token"
          },
          logout: { url: "/logout", method: "post" },
          user: { url: "/user", method: "get", propertyName: "user" } // I tried removing this line but then nuxt add the default route /api/auth/user something like that.
        }
        // tokenRequired: true,
        // tokenType: 'bearer'
      }
    }
  }
JoaoPedroAS51 commented 4 years ago

Hi @felipe-muner!

I would like to know, what's the point to have a endpoint user if I get all this information inside endpoints login.

The user endpoint is used to fetch the user if you refresh the page when logged. User data don't persist in storage.

To stop get error on request because I even didn't have the route on my server, I just created one route to satisfy the user endpoint and then I return null.

To disable user endpoint, set it to false

felipe-muner commented 4 years ago

Joao,

are you Brazilian? Me too kkkk but currently I'm travelling and I'm at Hanoi in Viet Nam.

Ok, I got you, even before the login(first page loaded) the system add the token to the authorization header(storaged inside thelocalstorage) and then call the user route(set up in nuxt.config.js). is it right? Ok then I go to the back-end(in my case node and I check with the Jwt.verify to validate the token and then nuxt will be expecting propertyName value to get this user and update the store and set it again in the localstorage... did you get me? I'm a bit not sure how is the flow cycle... Thank you so much my friend.

JoaoPedroAS51 commented 4 years ago

are you Brazilian? Me too kkkk but currently I'm travelling and I'm at Hanoi in Viet Nam.

Sou sim! :) Bah q legal, como é ai?

Even before the login(first page loaded) the system add the token to the authorization header(storaged inside thelocalstorage) and then call the user route(set up in nuxt.config.js). is it right?

Quando você carrega a página ele sincroniza o token e o adiciona ao Authorization header, porém se o usuário não está logado, o token deve estar como undefined ou false. Se estiver assim, ele não irá definir o header e também não irá chamar a rota do usuário até que o usuário esteja logado.

Ok then I go to the back-end(in my case node and I check with the Jwt.verify to validate the token and then nuxt will be expecting propertyName value to get this user and update the store and set it again in the localstorage

Isso, você checa se o token é valido. Se for, retorna o usuário. propertyName é a propriedade da resposta do back-end. No seu caso pelo que vi, você definiu propertyName: 'user', então a resposta em json deve ser { user: { ... } }. Depois o usuário é salvo dentro do state do Vuex.

Se o usuário recarregar a página, a rota do usuário será chamada novamente, pois os dados do usuário não persistem no armazenamento do Vuex.

felipe-muner commented 4 years ago

Já to morto aqui irmãozinho, muito obrigado mesmo por todo seu tempo e explicação detalhada.

Eu já to salvando o usuário após login na store.. tudo funcionou legal.. Minha dúvida é em relação essa chamada para a rota user antes do login. Amanhã vejo melhor, grande abraço.

Vou descansar e amanhã volto aqui pra enfrentar a fera. Eu to sem programar há 2 anos e venho viajando pela Europa e Asia... Podemos falar melhor por audio e te explico tudo que vc quiser saber...

JoaoPedroAS51 commented 4 years ago

Blz. Pode me adicionar no discord Joao Pedro AS51#1284

felipe-muner commented 4 years ago

Veja se foi... Sou o:

felipe.muner#0336

JoaoPedroAS51 commented 4 years ago

Foi, já aceitei :)

diegodelajara commented 4 years ago

Hi everyone! Can u help with an issue similar to this

I have my own custom strategy to get token, and all is good, but when a refresh page I lose user data and fetchUser does not works. It doesn´t send the params to API to get again the user data. the workflow is next: 1- send params to token api and get token 2- send params to login API to get the user

//nuxt.config.js

customStrategy: {
        _scheme: '~/schemes/covitScheme',
        endpoints: {
          login: {
            url: '/api/v1/token',
            method: 'post',
            propertyName: 'token',
            headers: {'x-channel-id': 1}
          },
          user: {
            url: '/api/v1/login',
            method: 'get',
            propertyName: false,
            headers: {'x-channel-id': 1}
          },
          logout: null
        }
      }

customScheme.js

import LocalScheme from '@nuxtjs/auth/lib/schemes/local'

export default class CustomScheme extends LocalScheme {

  _setToken (token) {
    if (this.options.globalToken) {
      // Set Authorization token for all axios requests
      this.$auth.ctx.app.$axios.setHeader(this.options.tokenName, token)
    }
  }

  _clearToken () {
    if (this.options.globalToken) {
      // Clear Authorization token for all axios requests
      this.$auth.ctx.app.$axios.setHeader(this.options.tokenName, false)
    }
  }

  mounted () {
    if (this.options.tokenRequired) {
      const token = this.$auth.syncToken(this.name)
      this._setToken(token)
    }
    return this.$auth.fetchUserOnce()

  }

  async login (endpoint) {
    if (!this.options.endpoints.login) {
      return
    }

    // Get token
    const result = await this.$auth.request({
      ...endpoint
    },
      this.options.endpoints.login
    )

    // Set token
    if (this.options.tokenRequired) {
      const token = this.options.tokenType
        ? this.options.tokenType + ' ' + result
        : result

      this.$auth.setToken(this.name, token)
      this._setToken(token)
    }

    // If result I get and set user
    if (result) {
      const user = await this.$auth.request({
        ...endpoint
      },
        this.options.endpoints.user
      )
      this.$auth.setUser(user);
    }
  }

  async fetchUser (endpoint) {
    // User endpoint is disabled.
    if (!this.options.endpoints.user) {
      this.$auth.setUser({})
      return
    }

    // Token is required but not available
    if (this.options.tokenRequired && !this.$auth.getToken(this.name)) {
      return
    }

    // Try to fetch user and then set
    try{
      const user = await this.$auth.requestWith(
        this.name,
        endpoint,
        this.options.endpoints.login
      )

      this.$auth.setUser(user)
    } catch (error){
      console.log(error)
    }
  }
}

When I set this.$auth.setUser(user) in login() method all is fine and app redirect me to /dashboard page and the user information (like role and email) is displayed on navBar but when I refresh page I lose user data. The app try to fetchUser but it give me a 400 error because user and password not sent. Another thing I don´t understand is Why endpoint parameter is undefined in async fetchUser (endpoint)??? . I think there is an issue in this part.

I hope u can help me Regards

RizaHKhan commented 3 years ago

Just pointing this out if anyone else ran into the same issue.

When I did this: this.$auth.setUser(response.data.user) it would not set the user. I had to do this: this.$auth.setUser({user: response.data.user})