auth-module: Login redirect not working, $auth.state.loggedIn false

In my login.vue submit method I have:

this.$auth.login({ data: this.form }) The correct endpoint is reached with the right values, a token is returned and saved correctly to localStorage. A follow up request to /user endpoint also executes successfully. All subsequent requests include the correct Authorization header.

However:

  • there is no redirect to the home page after login
  • $auth.state.loggedIn and $auth.state.user are both false/null, all secure pages (using middleware[‘auth’]) redirect to login It appears that the only thing working is the authorization header, vuex state is not being updated correctly.
<div align="right">This bug report is available on Nuxt.js community (#c44)</div>

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 28 (5 by maintainers)

Most upvoted comments

I think I see the issue: the endpoint doesn’t return a “user” object, rather the user itself is the payload:

{
    "email": "******@gmail.com",
    "id": 1,
    "username": "danjac"
}

This might explain why the user is not being set correctly, however loggedIn is not being set either (and here are no error messages).

EDIT: for the above issue I found this to be the solution:


    endpoints: {
     ....
      user: { url: '/api/auth/me/', propertyName: false },
    },

This fetches the user object as-is. Looks like all working well, thanks!

Encountered this today too and burned an hour debugging things. IMHO it is misleading/wrong for the propertyName to default to user even when specifying a custom endpoint config, including url. This propertyName: "user" default value seems based on odd assumptions of what is normal/typical.

This is also in conflict with the documentation, which states:

propertyName can be used to specify which field of the response to be used for value. It can be undefined to directly use API response or being more complicated like auth.user.

This does not work - omitting propertyName (thus making it undefined) or explicitly setting it as such (propertyName: undefined) will not work.

same on 4.9.1, really frustrating

getting same issue in @nuxtjs/auth: 4.8.1 after login it redirect /dashboard which is protected by auth when i refresh page it redirect to /login page but i am already logged when i go to / page appear user name on

Motivated by the laughable amount of hours spent while dealing with this library, especially this problem, I should comment here and open another issue afterwards, using the template for new issues.

The problem I was having

User accesses the login screen, types correct username/password combination, Auth module is configured with local strategy, fetches the endpoint for auth, gets a JWT token, fetches the user and nothing else happens. The user is stuck at /login.

Files

nuxt.config.js

auth: {
  strategies: {
    local: {
      endpoints: {
        login: { url: '/auth', method: 'post', propertyName: 'token' },
        logout: false,
        user: { url: '/auth/user', method: 'get', propertyName: false }
      }
    }
  },
  redirect: {
    login: '/login',
    logout: '/login',
    home: '/'
  }
},

pages/login.vue (only the JS)

<script>
import _ from 'lodash';

export default {
    layout: 'login',
    data () {
        return {
            error: null,
            username: '',
            password: ''
        }
    },
    computed: {
        redirect() {
            return decodeURIComponent(_.get(this, '$route.query.redirect', '/'));
        }
    },
    methods: {
        async submitLogin (e) {
            e.preventDefault();
            this.error = null;

            const test = await this.$auth.loginWith('local', {
                data: {
                    username: this.username,
                    password: this.password
                }
            }).catch(err => {
                if (!err.response) return;

                switch (err.response.status) {
                    case 403:
                    case 401:
                        this.error = 'Incorrect username/password';
                        break;
                    case 417:
                        this.error = 'Unauthorized.'
                        break;
                    default:
                        this.error = JSON.stringify(err); // Not exactly correct, but I was debugging a lot
                        break;
                }
            });

            return test;
        }
    }
}
</script>

I just followed the demos present in the source. My @nuxtjs/auth current version is 4.9.0.

Debugging the Vuex state, I found some very interesting things:

  • When return test is executed at submitLogin(), $auth.loggedIn is false and $auth.user is null. Curiously, if I manually go to an address where my user is authorized by the business rules, after the login attempt, the screen opens normally and all the states are normal ($auth.loggedIn is true and $auth.user is defined and valid);
  • If I execute await this.$auth.logout();, the screen doesn’t redirect to /login afterwards. In truth “nothing” happens. However, if I navigate to any route that require auth middleware (the user logged in), I see the /login screen again;

I dug into the code and I’ve found this and this (that basically use Promises) and the strategy’s login implementation (that uses await). So now it’s clear that:

  • The local strategy, if it is slow enough, can’t update the user state before the library tries the redirect, which checks for loggedIn == false, which in turn just keeps the user in the same login screen (the feeling that nothing happened);
  • There’s no way (not by the actual docs and examples) to make the redirect wait for the token retrieval.

In order to make it work, the only thing I can do is to test the return from this.$auth.loginWith('local'), to verify if I got, for instance, a 2XX status code, and then redirect the user manually.

So I modified my submitLogin method as follows:

methods: {
    async submitLogin (e) {
        e.preventDefault();
        this.error = null;

        const test = await this.$auth.loginWith('local', {
            data: {
                username: this.username,
                password: this.password
            }
        }).catch(err => {
            if (!err.response) return;

           switch (err.response.status) {
                case 403:
                case 401:
                    this.error = 'Incorrect username/password';
                    break;
                case 417:
                    this.error = 'Unauthorized.'
                    break;
                default:
                    this.error = JSON.stringify(err); // Will still keep this for now.
                    break;
            }
        });

        if (_.get(test, 'status') == 200) {
            window.location.replace(_.get(this, '$route.query.redirect', '/'));
        }
    }
}

This solves half of my problem. For the logout, I had to do something like this:

    methods: {
        logout: async function() {
            await this.$auth.logout();
            window.location.reload(false); 
        }
    }

But refreshing the current page to cause a redirect to login has a problem: some of my pages have things executing on asyncData, which executes before the browser starts to redirect to the /login route, and flashes 401 messages to my user. To guarantee my user will not see these 401’s, I had to create a global middleware (registered in nuxt.config.js) with the following:

export default function ({ $auth, redirect }) {
    if (!$auth.user) {
        return redirect('/login?redirect=/');
    }
}

Finally, I had to implement a redirect plugin (/plugins/auth.js) to get the redirections, since the redirect supported implemented in this library doesn’t work properly:

plugins/auth.js

export const isSameURL = (a, b) => a.split('?')[0] === b.split('?')[0]

export const isRelativeURL = u =>
  u && u.length && /^\/[a-zA-Z0-9@\-%_~][/a-zA-Z0-9@\-%_~]*[?]?([^#]*)#?([^#]*)$/.test(u)

export default function ({ app }) {
  const redirect = function (name, noRouter = false) {
    if (!this.options.redirect) {
      return;
    }

    const from = this.options.fullPathRedirect ? this.ctx.route.fullPath : this.ctx.route.path;

    let to = this.options.redirect[name]
    if (!to) {
      return;
    }

    // Apply rewrites
    if (this.options.rewriteRedirects) {
      if (name === 'login' && isRelativeURL(from) && !isSameURL(to, from)) {
        this.$storage.setUniversal('redirect', from);
      }

      if (name === 'home') {
        const redirect = this.$storage.getUniversal('redirect') || this.ctx.route.query.redirect
        this.$storage.setUniversal('redirect', null);

        if (isRelativeURL(redirect)) {
          to = redirect;
        }
      }
    }

    // Prevent infinity redirects
    if (isSameURL(to, from)) {
      return
    }

    if (process.browser) {
      if (noRouter) {
        window.location.replace(to);
      } else {
        this.ctx.redirect(to);
      }
    } else {
      this.ctx.redirect(to, { ...this.ctx.route.query, redirect: from })
    }
  }

  app.$auth.redirect = redirect.bind(app.$auth)
}

Funny fact, this code is implemented here.

Final nuxt.config.js

auth: {
  plugins: ['~/plugins/auth.js'],
  strategies: {
    local: {
      endpoints: {
        login: { url: '/auth', method: 'post', propertyName: 'token' },
        logout: false,
        user: { url: '/auth/user', method: 'get', propertyName: false }
      }
    }
  },
  redirect: {
    login: '/login',
    logout: '/login',
    // callback: '/',
    home: '/'
  }
},

My code is working fine on my desktop ubuntu. But when I put code into remote server (ubuntu too) it didn’t set loggedIn and user just like write above.

I set my user propertyName to false, but it still didn’t work.

Any suggestions?

my this.$auth.loggedIn is always FALSE, please help!!! I am logging in, getting a valid jwt token and getting user details back in the response!

here is my NUXT auth version: “@nuxtjs/auth-next”: “^5.0.0-1610115973.9fdaa66”,

and here is my nuxt.config.js

export default {
  // Global page headers (https://go.nuxtjs.dev/config-head)
  head: {
    title: 'choosday',
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: '' }
    ],
    link: [
      { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }
    ]
  },

  // Global CSS (https://go.nuxtjs.dev/config-css)
  css: [
    '~assets/scss/tailwind.scss',
  ],

  // Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
  plugins: [
  ],

  // Auto import components (https://go.nuxtjs.dev/config-components)
  components: true,

  // Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
  buildModules: [
    // https://go.nuxtjs.dev/tailwindcss
    '@nuxtjs/vuetify',
    '@nuxtjs/tailwindcss',
  ],
  ssr: true,
  // Modules (https://go.nuxtjs.dev/config-modules)
  modules: [
    '@nuxtjs/axios',
    '@nuxtjs/auth-next'
  ],
  axios: {
    baseURL: 'http://choosapi.test/api'
    // baseURL: 'http://movemeapi.test/api'
  },
  // Build Configuration (https://go.nuxtjs.dev/config-build)
  build: {
    vendor: ['axios', 'vuetify']
  },
  auth: {
    strategies: {
      local: {
        token: {
          property: 'token',
          required: true,
          type: 'bearer'
        },
        user: {
          property: 'user',
          autoFetch: true
        },
        endpoints: {
          login: { url: 'auth/login', method: 'post', propertyName: 'token' },
          logout: { url: 'logout', method: 'get', propertyName: 'token' },
          user: { url: 'auth/profile', method: 'get', propertyName: 'false' },
        }
      }
    }
  },
}

Me too, just simple middleware

guest.js

export default function ({ store, redirect, router }) {
   console.log(store.state.auth)
   if (store.state.auth.loggedIn) {
      return redirect('/dash');
   }
}

But in ssr it’s always false like this image