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

Created on 7 Feb 2018  路  27Comments  路  Source: nuxt-community/auth-module

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.

This bug report is available on Nuxt.js community (#c44)
solved

Most helpful comment

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!

All 27 comments

To confirm, there is no change to vuex state after a successful login. Store index.js is just empty:

export default {
  state: () => ({

  })
}

Hi @danjac, thank you for contributing to auth-module 馃槃

For future reference, maybe related to #55 @pi0

Could you provide some essential informations :

  • Which operating system are you using ?
  • Which version of auth-module are you using ?
  • How did you manage to get this error ? (code reproduction)
  • How did you run your application ? (SSR / SPA / ...)
  • Which Nuxt variant are you using ? (Vanilla, using class-component through Babel, using Typescript ?)

I tried to reproduce on our examples, but couldn't do you have a custom Vuex store, if yes, what is its contents.

Thank you very much 馃憤

Hi, thanks for fast response!

Full client code here:

https://github.com/danjac/spacenews-ui

Version: latest install (npm install @nuxtjs/auth)
OS: Linux/Fedora 27
Running on SSR

Thanks @danjac !

We are going to investigate the issue asap with @pi0

@danjac

You set user fetch endpoint as this:

user: { url: '/api/auth/me/' },

By default, the module is using these options:

user: { method: 'get', propertyName: 'user' }

Could you please enforce your propertyName to your user object path/name and tell me how it reacts?

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!

@danjac Still it should be reported to the user anyway :)

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 problem with redirecting after positive login.

If I try to connect to protected pages, like /, redirect me to /users/login but after I logged in, I don't get redirected.

this.$auth.token has the right value and this.$auth.loggedIn is true.

login () {
      this.$auth.login({
        data: this.data,
      })
        .then((res) => {
          console.log('Auth Success')
          console.log(this.$auth.token)
          console.log(this.$auth.state.loggedIn)
        })
        .catch((err) => {
          console.log(err)
          this.$notify({
            title: 'Important message',
            text: err
            })

        })
      }
auth: {
    endpoints: {
      login: { url: '/auth/login', method: 'post', propertyName: 'token' },
      logout: { url: '/auth/logout', method: 'post' },
      user: { url: '/auth/user', method: 'get', propertyName: 'user' }
    },

    redirect: {
      login: '/users/login',
      home: '/'
    },

    cookie: false,
  },

  router: {
    middleware: ['auth']
  },

Fixed as of 4.0 馃憤

Please create a new issue if it happens again in 4.0 :)

Having the same issue here, using "@nuxtjs/auth": "^4.5.1"
@danjac your workaround did the job, thx

It is reproduces using mode: 'universal' instead of spa.

still in 4.5.3
setting user: { url: '/api/auth/me/', propertyName: false }, worked thanks @danjac

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?

After switching the mode from "Universal" to "SPA", login and redirection work correctly in @nuxtjs/auth: 4.5.3

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

@imsidz Same here on v4.8.2

same on 4.9.1, really frustrating

function IssueTracking.Accounts.Auth.login/2 is undefined (module IssueTracking.Accounts.Auth is not available)
help me

defmodule IssueTrackingWeb.SessionController do
use IssueTrackingWeb, :controller
alias IssueTracking.Repo
alias IssueTracking.Accounts.Auth

def new(conn, _params) do
render(conn, "new.html")
end

def create(conn, session_params) do
case Auth.login(session_params, Repo) do
{:ok, user} ->
conn
|> put_session(:current_user, user.id)
|> put_flash(:info, "logged in successfully.")
|> redirect(to: Routes.page_path(conn, :index))
:error ->
conn
|> put_flash(:error, "There was a problem with your username/password")
|> render("new.html")
end
end
error coming like function IssueTracking.Accounts.Auth.login/2 is undefined (module IssueTracking.Accounts.Auth is not available)
help me
def delete(conn, _params) do
conn
|> delete_session(:current_user_id)
|> put_flash(:info, "Signed out successfully.")
|> redirect(to: Routes.page_path(conn, :index))
end
end

def new(conn, _params) do
render(conn, "new.html")
end

def create(conn, session_params) do
case Auth.login(session_params, Repo) do
{:ok, user} ->
conn
|> put_session(:current_user, user.id)
|> put_flash(:info, "logged in successfully.")
|> redirect(to: Routes.page_path(conn, :index))
:error ->
conn
|> put_flash(:error, "There was a problem with your username/password")
|> render("new.html")
end
end

def delete(conn, _params) do
conn
|> delete_session(:current_user_id)
|> put_flash(:info, "Signed out successfully.")
|> redirect(to: Routes.page_path(conn, :index))
end
end

error coming like function IssueTracking.Accounts.Auth.login/2 is undefined (module IssueTracking.Accounts.Auth is not available)
help me

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: '/'
  }
},

After two days, I finally found the solution.

If the response from the login request doesn't have the (user information object), and user information needs to obtained from another api request, your case is looks like mine.

  1. My nuxt.config.js :

strategies: {
local: {
token: {
property: 'access_token',
},
user: {
property: 'data',
autoFetch: false,
},
endpoints: {
login: {
url: '/auth/login',
method: 'post',
},
logout: {
url: '/auth/logout',
method: 'get',
},
user: {
url: '/user/info',
method: 'get',
propertyName: false,
},
},

* NOTE the (autoFetch) property of (user) object

  1. My login request:

    async login() {
    await this.$auth
    .loginWith('local', {
    data: {
    username: data.phone,
    password: data.password,
    ...
    },
    })
    .then((response) => {
    ...
    dispatch('getUserInfo')
    ...
    })
    .catch((error) => {...})
    },

* The response of login request is set as (token)

* Then the (getUserInfo) function is called

  1. My getUserInfo function:

getUserInfo({ commit }) {
return this.$axios
.$get(${process.env.baseUrl}/user/info)
.then((response) => {
this.$auth.setUser(response.data)
...
.catch((err) => {... })
},

* The response of getUserInfo request is set as ($auth.user) object in store

--------------------------------------------------------------------------------

Now ($auth.loggedIn) is true and ($auth.user) is ready to use, also after reloading the page, these information are not lost and disappeared.

I hope I was able to help at least one person

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' },
        }
      }
    }
  },
}

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' },
        }
      }
    }
  },
}

any luck - the above didn't help me either (setting user info)

@jumptrnr @jbiddulph This worked for me https://github.com/nuxt-community/auth-module/issues/999#issuecomment-760329564

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

Was this page helpful?
0 / 5 - 0 ratings