nuxt-community / auth-module

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

Redirect not working after login or logout #205

Closed Geminii closed 6 years ago

Geminii commented 6 years ago

Version v4.5.1

Reproduction link https://github.com/nuxt-community/auth-module

Configuration

auth: {
    // Options
    strategies: {
      local: {
        endpoints: {
          login: { url: process.env.BASE_URL + '/auth', method: 'post', propertyName: 'token' },
          logout: false,
          user: { url: process.env.BASE_URL + '/users/auth', method: 'get', propertyName: false }
        }
      }
    },
    redirect: {
      login: '/signin',
      logout: '/signin',
      home: '/home'
    },
    watchLoggedIn: true,
    rewriteRedirects: false,
    resetOnError: true
  }

Action to login :

this.$auth.loginWith('local', {
            data: {
              username: this.username,
              password: this.password
            }
          });

Action to logout : await this.$auth.logout();

Steps to reproduce I use local strategy with a token. After the token expires if i reload the page, the user is logged out because i have resetOnError=True. I'm redirect to my signin page. Then i try to reconnect, i get new token and when i check into VueX, i got this result :

auth: {
busy:false,
loggedIn:true,
strategy:"local",
user: { ... }
 }

But i'm not redirect to home page ... If i refresh, i'm connected into the home page.

Where i'm doing wrong ? Thanks for your answers.

Regards,

ghost commented 6 years ago

This issue as been imported as question since it does not respect auth-module issue template. Only bug reports and feature requests stays open to reduce maintainers workload. If your issue is not a question, please mention the repo admin or moderator to change its type and it will be re-opened automatically. Your question is available at https://cmty.app/nuxt/auth-module/issues/c155.

ribrewguy commented 6 years ago

@Geminii I'm running into the same issue. Did you ever find the cause?

NoahCardoza commented 6 years ago

Me too, I have spent so much time with this I could have implemented my own auth system by now... I was hoping this would be more intuitive.

NoahCardoza commented 6 years ago

I came up with this which seems to work as I would have expected the redirects to work out of the box.

export default function ({ app }) {
  const oldLogout = app.$auth.logout.bind(app.$auth)
  const oldLogin = app.$auth.login.bind(app.$auth)

  app.$auth.logout = (...args) => {
    const _ = oldLogout(...args)
    _.then(() => app.$auth.redirect('logout'))
    return _
  }

  app.$auth.login = (...args) => {
    // sometimes doesn't work when the user tries to get to the admin page
    // before being logged in.

    const _ = oldLogin(...args)
    _.then(() => {
      app.$auth.redirect('home')
    })
    return _
  }
}
sayx commented 6 years ago

I came up with this which seems to work as I would have expected the redirects to work out of the box.

export default function ({ app }) {
  const oldLogout = app.$auth.logout.bind(app.$auth)
  const oldLogin = app.$auth.login.bind(app.$auth)

  app.$auth.logout = (...args) => {
    const _ = oldLogout(...args)
    _.then(() => app.$auth.redirect('logout'))
    return _
  }

  app.$auth.login = (...args) => {
    // sometimes doesn't work when the user tries to get to the admin page
    // before being logged in.

    const _ = oldLogin(...args)
    _.then(() => {
      app.$auth.redirect('home')
    })
    return _
  }
}

@NoahCardoza Where did you make these changes in the configuration?

ConsoleTVs commented 6 years ago

This lirbary seems dead. it's faster to create a few methods and states on vuex and manage the stuff alone...

NoahCardoza commented 6 years ago

@sayx I added this as an auth-module plugin.

nuxt.config.js

...
  auth: {
    plugins: ['~/plugins/auth.js'],
    strategies: {
      local: {
        endpoints: {
          login: { url: '/api/login', method: 'post' },
          logout: { url: '/api/logout', method: 'get' },
          user: { url: '/api/user', method: 'get', propertyName: 'username' }
        },
        tokenRequired: false,
        tokenType: false,
      }
    },
    redirect: {
      login: '/login',
      logout: '/login',
      home: '/admin',
      user: '/admin',
    },
  }
...

~/plugins/auth.js being what I shared earlier.

@ConsoleTVs I feel like the library is useful, it just didn't seem to function the way I thought it would.

dzsessona commented 6 years ago

I am having the same problem,

  auth: {
    rewriteRedirects: true,
    strategies: {
      local: {
        endpoints: {
          login: { url: 'user/login', method: 'post', propertyName: 'data.token' },
          user: { url: 'user/me', method: 'get', propertyName: 'data' },
          logout: false
        }
      },
      facebook: {
        client_id: process.env.FB_APP_ID,
        userinfo_endpoint: 'https://graph.facebook.com/v2.12/me?fields=about,name,picture{url},email',
        scope: ['public_profile', 'email'],
        redirect_uri: process.env.FB_REDIRECT_URI
      },
      google: {
        client_id: process.env.GOOGLE_APP_ID,
        scope: ['profile', 'email'],
        redirect_uri: process.env.GOOGLE_REDIRECT_URI
      },
    },
    redirect: {
      login: '/login',
      logout: '/',
      home: '/privacy',
    }
  },

A guarded route profile, when trying to access it withought being logged in it redirects to the login page, after login (on any strategy... local or oauth) it always redirects to /privacy instead of the original guarded route as per documentation.

Please note that even by default rewriteRedirects should be true i am making sure of it in the config options.

sfgn commented 6 years ago

I can't actually get the auth module to redirect after login. It just never emits the routeChange event after I log in it seems. I can get it to redirect on logout, or from an authed page when not logged in.

auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/login',
            method: 'get',
            propertyName: 'token'
          },
          logout: {
            url: '/logout',
            method: 'delete'
          },
          user: {
            url: '/me',
            method: 'get',
            propertyName: 'data'
          }
        }
      }
    },
    redirect: {
      login: '/auth/login',
      logout: '/',
      home: '/',
      callback: '/'
    },
    watchLoggedIn: true,
    rewriteRedirects: true
  },

I'm still not sure what callback is for in redirects, but I've changed it to every possible value I can think of, as well as omitting it, with no difference.

I've tried running as SSR and SPA, with no difference as well.

shawnlauzon commented 6 years ago

Duplicate of #134. See workaround in that thread which resolved the problem for me.

AllanPinheiroDeLima commented 5 years ago

I actually found that when you set the property user on endpoints to false, it starts working as expected.

The thing is that is seems that the nuxt auth plugin waits this property to be defined to redirect to homepage. There is no need to make workarounds, but a PR may be welcome.

Here is my code:

auth: {
strategies: {
      local: {
        endpoints: {
          login: { url: '/auth/login', method: 'post', propertyName: 'token' },
          user: { url: '/auth/user', method: 'get', propertyName: false },
          logout: { url: '/auth/logout', method: 'post' }
        },
        // tokenRequired: true,
        // tokenType: 'Bearer'
      }
    },
    redirect: {
      login: '/public/login',
      home: '/',
      logout: '/public/login'
    }
  }
pirmax commented 5 years ago

I've the same problem.

I'm using auth-module and when submitting the login form, I am redirected to the same login page, not to the requested page via a $router.push()...

Someone has an idea please?

async submitLogin() {
    try {
      // @ts-ignore
      await this.$auth.loginWith('local', {
        data: {
          email: this.email,
          password: this.password,
        }
      })

      console.log('on push ici');
    } catch (e) {
      this.error = e.response.data.message
    }
  }
brianshepanek commented 5 years ago

I think the issue is related to the default redirects.

If the redirects for 'logout' and 'home' are the same (as is the default), the internal redirect function gets confused because after a successful login, it always redirects to 'home' first and then checks for a stored redirect path if 'rewriteRedirects' is set to true.

However, if 'logout' and 'home' are the same path, the name of the path is set to 'logout' not 'home' so the actual redirect you want never occurs.

Changing the redirect for 'logout' seems to fix this issue.

{
  auth: {
    redirect: {
      logout: '/logout',
      home: '/'
    }
  }
}

Note that this needs to be a distinct path, the same path with query parameters, even with 'fullPathRedirect' set to true does not appear to fix this.

blackinitial commented 5 years ago

@AllanPinheiroDeLima thanks for the solution, my auth now work succesfully.

samcyn commented 5 years ago

I got the same issue but got it fixed by put a forward slash in front of my urls redirect: { login: '/public/login/', home: '/', logout: '/public/login/' } instead of redirect: { login: '/public/login', home: '/', logout: '/public/login' } N.B: Notice the forward slash.

iftieaq commented 5 years ago

Using the promise handler worked for me.

let data = {
  username: this.username,
  password: this.password
}

this.$auth.login({data: data})
    .then(() => {
        this.$router.push("/dashboard")
    })
    .catch((error)=> {
        console.log(error)
    })
shouvikfullstack commented 4 years ago

Thank you @NoahCardoza, your solution worked like champ! :)

caroait commented 4 years ago

I was fighting with this error too and I solve it adding this line before redirect to the login

$auth.$storage.setUniversal('redirect', path)

donmb1 commented 4 years ago

I found this very hard to understand and its still not working as expected. Tried all the suggested solutions above. I will probably start building my own auth using a bit of vuex :/

samcyn commented 4 years ago

@donmb1 that's what I had to do then but with the recent updates and fixes in the latest version. I think you can find your way around this. Last Year I fixed this issue using the solution quoted below.. but due to some other requirements for my auth system I had to build an Auth system customized for my application. If you've the time I'll suggest you take a break and get back on it later. Use the latest version as well. You never know until you give it all. Stay safe.

I got the same issue but got it fixed by put a forward slash in front of my urls redirect: { login: '/public/login/', home: '/', logout: '/public/login/' } instead of redirect: { login: '/public/login', home: '/', logout: '/public/login' } N.B: Notice the forward slash.

westofer commented 4 years ago

@donmb1

I came up with this which seems to work as I would have expected the redirects to work out of the box.

export default function ({ app }) {
  const oldLogout = app.$auth.logout.bind(app.$auth)
  const oldLogin = app.$auth.login.bind(app.$auth)

  app.$auth.logout = (...args) => {
    const _ = oldLogout(...args)
    _.then(() => app.$auth.redirect('logout'))
    return _
  }

  app.$auth.login = (...args) => {
    // sometimes doesn't work when the user tries to get to the admin page
    // before being logged in.

    const _ = oldLogin(...args)
    _.then(() => {
      app.$auth.redirect('home')
    })
    return _
  }
}

Worked for me

see https://github.com/nuxt-community/auth-module/issues/205#issuecomment-424385896

westofer commented 4 years ago

YES Finally just remove auth: false from your login.vue

   export default {                                                                                                   
     components: {},                                                                                                  
      data() {                                                                                                         
        return {                                                                                                       
          login: {                                                                                                     
            username: '',                                                                                              
            password: '',                                                                                              
          },                                                                                                           
        }                                                                                                              
      },                                                                                                               
      // auth: false, <========================                                                                                                  
      methods: {                                                                                                       
        async userLogin() {                                            
...
...
...
AllanPinheiroDeLima commented 4 years ago

They seem to be moving to nuxt 3.0 though. I think maybe we should abandon this implementation next few months, Link to the new docs: https://dev.auth.nuxtjs.org/guide

donmb1 commented 4 years ago

I ended up building my own auth. As simple as fetching jwt token, writing to a localStorage and making a top-level request which decides if I am logged in or not.

ConsoleTVs commented 4 years ago

If you're not using http only cookies for auth on the web, you're vulnerable to xss. Does not matter if u save the token in localstorage, memory or nom-http only cookies.

The only way is to have a server http only cookie with the session information. This requires the api and app to be in the same tld, but it's mostly the case anyway.

kamerat commented 3 years ago

I had the same issues and fixed it the following way:

May people suggested setting propertyName: false as @AllanPinheiroDeLima suggested:

endpoints: {
    login: { url: '/api/login', method: 'post' },
    logout: { url: '/api/logout', method: 'post' },
    user: { url: '/api/me', method: 'get', propertyName: false },
},

However, seems this did nothing for me, the user was still undefined in vuex (remember to activate vuex by adding index.js in stores folder)

what worked for me was to use the user object and set property to false. This is probably the new way to do the suggestion @AllanPinheiroDeLima suggested.

local: {
    endpoints: {
        login: { url: '/api/login', method: 'post' },
        logout: { url: '/api/logout', method: 'post' },
        user: { url: '/api/me', method: 'get' },
    },
    user: {
        property: false, // <---
    },
    token: {
        property: 'access_token',
        required: true,
        type: 'Bearer',
    },
},
luixal commented 3 years ago

This is kind of weird... my redirect config was like this:

redirect: {
  login: '/login',
  logout: '/login',
  home: '/',
  callback: '/'
}

And my problem was that when users came to the page, the stayed (not logged in) in the / page, they were not redirected to /login. Once I removed the callback redirect, it works like a charm.

My complete auth config is this:

auth: {
    strategies: {
      local: {
        endpoints: {
          login: {
            url: '/auth/local',
            method: 'post',
            propertyName: 'jwt'
          },
          user: {
            url: '/users/me',
            method: 'get',
            propertyName: false
          },
        }
      }
    },
    redirect: {
      login: '/login',
      logout: '/login',
      home: '/'
    }
  }
KunalAggarwal commented 3 years ago

I have been struggling, for days now, with redirects not working after login or logout. My setup was simple. Started a new nuxt project (SSR/Universal, with Axios, PWA), added nuxt auth-next, set auth as middleware for router in nuxt.config.js. pages/index.vue is not accessible without login. Created pages/login.vue and set auth: "guest" to allow login page to open only when not logged in.

Following is my auth config in nuxt.config.js

auth: {
    redirect: {
        login: "/login",
        logout: "/login",
        home: "/"
    },
    watchLoggedIn: true,
    strategies: {
        local: {
            token: {
                property: "jwt"
            },
            user: {
                property: false
            },
            endpoints: {
                login: {
                    url: "auth/local",
                    method: "post"
                },
                user: {
                    url: "users/me",
                    method: "get"
                },
                logout: false
            }
        }
    }
}

With this setup, when login using local strategy, after successful login redirection didn't work and I had to refresh the page for user to redirect to /.

this.$auth
    .loginWith("local", {
        data: {
            username: this.username,
            password: this.password
        }
    })
    .then(response => {
        console.log(response);
    })
    .catch(error => {
        console.log(error.response);
    });

At this point, I am at the brink of pulling my hair out from my head in frustration. Then magic happens.

I created a store/index.js with the following code:

export const state = () => ({});

export const mutations = {};

export const getters = {};

export const actions = {};

basically, just a blank store.

Run the app again, and voila, redirection starts to work! Login, logout, everything works.

Hope this helps someone!

JoaoPedroAS51 commented 3 years ago

Hi @KunalAggarwal! Thank you for reporting :) From now on users will be warned that Vuex Store is not activated #1195

oussla commented 3 years ago

Hello, thanks a lot @KunalAggarwal for your analysis and method. As the solution seems to activate the Vuex module, placing an empty JS file in store/ folder does the trick. No code needed, and redirections work.

https://nuxtjs.org/docs/2.x/directory-structure/store#activate-the-store Nuxt.js will look for the store directory. If it contains a file, that isn't a hidden file or a README.md file, then the store will be activated.

Thanks again !

timdrake commented 3 years ago

I was hoping the auth-next package would handle things more gracefully than its' predecessor, but this does not seem to be the case. However, I was able to get everything working well, and without throwing navigation guard errors. My primary objective was to redirect the user back to the guarded path (including parameters) after a successful login.

An overview of my routes, and the desired redirect behavior is as follows:

If the user IS NOT authenticated the following should occur:

/ => /login
/login (no redirect occurs)
/sign-up (no redirect occurs ; the user needs to be able to sign up if they do not have a user account)
/logout (no redirect occurs)
/home => /login
/guarded/path => /login
/another/guarded/path?with= parameters => /login?with= parameters (the ?with=parameters displaying on the login page looks silly, and can likely be removed using a custom auth plugin via $auth.onRedirect. I just have not done this, yet. I will update this comment if I do.)

If the user IS authenticated the following should occur:

/ => /home
/login => /home
/sign-up => /home
/logout (no redirect occurs)
/home (no redirect occurs)
/guarded/path (no redirect occurs)
/another/guarded/path?with= parameters (no redirect occurs)

My auth relevant nuxt.config.js is as follows:

auth: {
  redirect: {
    // do not allow auth to automatically redirect to home. we will do this with our custom auth-redirect middleware.
    home: false,
    login: '/login',
    logout: '/logout',
  },
  strategies: {
    local: {
      token: {
        property: 'jwt',
        global: true,
      },
      user: {
        property: false,
        autoFetch: false,
      },
      endpoints: {
        login: { url: '/auth/local', method: 'post' },
        logout: { url: '/auth/logout', method: 'post' },
        user: { url: '/users/me', method: 'get' },
      },
    },
  },
},

// enable auth globally. we want to guard everything by default, and override on a case-by-case basis.
router: {
  middleware: ['auth'],
},

My custom middleware/auth-redirect.js middleware is as follows:

export default function ({ $auth, redirect, route }) {
  // if the user is not authenticated, and we are not on the login or sign up page then redirect to the login page.
  if (!$auth.loggedIn && !['/login', '/sign-up'].includes(route.path)) {
    return redirect('/login')
  // if the user is authenticated, and we are not on the home page then redirect to the home page.
  } else if ($auth.loggedIn && route.path !== '/home') {
    return redirect('/home')
  }
}

My / auth relevant logic is as follows:

<script>
export default {
  auth: false,
  middleware: 'auth-redirect',
}
</script>

My /login auth relevant logic is as follows:

<script>
export default {
  beforeRouteEnter(from, to, next) {
    // attempt to store the route full path (including parameters) so that we may redirect to it after a successful login.
    next((vm) => vm.setRedirectUrl(to))
  },
  auth: false,
  middleware: 'auth-redirect',
  data() {
    return {
      redirectUrl: null,
    }
  },
  methods: {
    setRedirectUrl(toRoute) {
      // only store the route full path (including parameters) if the path of the route does not match one of the following. we do not want to redirect a successful login back to any of the following pages.
      if (!['/', '/login', '/logout', '/sign-up'].includes(toRoute.path)) {
        this.redirectUrl = toRoute.fullPath
      }
    },
    async signinUser(userInfo) {
      const userData = {
        /* credentials go here */
      }

      try {
        const response = await this.$auth.loginWith('local', { data: userData })

        await this.$auth.setUserToken(response.data.jwt)

        // if a redirect URL is set then redirect to the URL.
        if (this.redirectUrl) {
          this.$router.push(this.redirectUrl)
        // if a redirect URL is not set then redirect to the home page.
        } else {
          this.$router.push('/home')
        }
      } catch (e) {
        /* exception handling goes here */
      }
    },
  },
}
</script>

My /sign-up auth relevant logic is as follows:

<script>
export default {
  auth: false,
  middleware: 'auth-redirect',
}
</script>

My /logout auth relevant logic is as follows:

<script>
export default {
  auth: false,
  created() {
    if (this.$auth.loggedIn) {
      this.$auth.logout()
    }
  },
  mounted() {
    this.$router.app.refresh()
  },
}
</script>

Lastly, do not forget to initialize the Vuex store by at least creating an empty store/index.js file. Redirects as well as many facets of auth seem to have issues when the Vuex store is not enabled.

My /guarded/paths need no auth relevant logic as auth has been enabled globally in nuxt.config.js.

I hope this helps. 🙏🏻

vencho-mdp commented 3 years ago

I had this problem. I'm using the refresh token scheme and what it worked for me was to, after calling the loginWith method, write

await this.$auth.setUserToken(
          response.data.token,
          response.data.refreshToken
        );

It might be redundant, but it's the fix I found. Hope it helps!

leduyhoang1994 commented 3 years ago

I have been struggling, for days now, with redirects not working after login or logout. My setup was simple. Started a new nuxt project (SSR/Universal, with Axios, PWA), added nuxt auth-next, set auth as middleware for router in nuxt.config.js. pages/index.vue is not accessible without login. Created pages/login.vue and set auth: "guest" to allow login page to open only when not logged in.

Following is my auth config in nuxt.config.js

auth: {
    redirect: {
        login: "/login",
        logout: "/login",
        home: "/"
    },
    watchLoggedIn: true,
    strategies: {
        local: {
            token: {
                property: "jwt"
            },
            user: {
                property: false
            },
            endpoints: {
                login: {
                    url: "auth/local",
                    method: "post"
                },
                user: {
                    url: "users/me",
                    method: "get"
                },
                logout: false
            }
        }
    }
}

With this setup, when login using local strategy, after successful login redirection didn't work and I had to refresh the page for user to redirect to /.

this.$auth
    .loginWith("local", {
        data: {
            username: this.username,
            password: this.password
        }
    })
    .then(response => {
        console.log(response);
    })
    .catch(error => {
        console.log(error.response);
    });

At this point, I am at the brink of pulling my hair out from my head in frustration. Then magic happens.

I created a store/index.js with the following code:

export const state = () => ({});

export const mutations = {};

export const getters = {};

export const actions = {};

basically, just a blank store.

Run the app again, and voila, redirection starts to work! Login, logout, everything works.

Hope this helps someone!

It's work ! Thank you so much !

issamlk1 commented 2 years ago

window.location.reload() as simple solution and it was the only one that worked for me,

based on @vencho-mdp answer I believe that my problem happened because I am using "user: false" so my user object is {} and maybe this is the reason if someone from the development team interested in fixing this issue.

My problem now is that the pwa is freezing for some reason ig because of the redirection but am gonna search for that somewhere else

Edit: I found the problem with pwa, it was the same solution that I've suggested, so if you're planing to run pwa you can use plain javascript redirect with: location.href = '/ location.href = '/login in the promise of login and logout methods

Edit 2: pwa is freezing on production server even tho it worked on production on local host still debugging it

imprisoner commented 2 years ago

In my case issue was around cookie: false, setting it to true did the trick. I also noticed that using just localStorage has influence at watching loggedIn prop. Without cookies I solved the problem manually redirecting after login with $router.push and manual redirecting in mounted on 'guest' routes if loggedIn: true , but reloading page on a protected route redirects me to login page, and then to 'home'. Decided to keep cookies true :)

mimbar commented 2 years ago

My issue fixed by set the auth: 'guest' in login.vue and everything works.

aminovmunir commented 1 year ago

@NoahCardoza solution is worked for me. Thx!