How to perform a SignOut action in Nuxt.js - vue.js

I'm currently working on porting the following router.beforeEach action that I have for a Vue.js application to something workable within Nuxt.js.
I've had a good trawl of the middleware documentation, but I'm not quite sure what the correct pattern would be to follow.
My callback which runs before every route change in my Vue.js application is as:
// This callback runs before every route change, including on page load.
router.beforeEach(async (to, from, next) => {
// Reset All State When A User Logs Out:
if (to.redirectedFrom === '/sign-out') {
store.dispatch('auth/resetAuthState')
Vue.prototype.authAPI.cleanseLocalStorage()
}
if (to.meta.authenticationRequired) {
if (!store.getters['auth/activeUserIsAuthenticated']) {
next({ name: 'signIn' })
} else {
next()
}
} else {
next()
}
})
I have the following redirect in my Vue router to perform the redirect action:
...
{
path: '/sign-out',
name: 'signOut',
redirect: {
name: 'signIn'
},
meta: {
...{
authenticationRequired: false,
sitemap: {
ignoreRoute: true
}
}
}
},
...
So on a SignOut redirect, I cleanse local storage and do some further state management inside the Vuex store.
However, I have no idea where to start this with Nuxt.js - any ideas would be greatly appreciated.

I suggest that using "Nuxt/auth" for handling your authentication. you can read this docs here:
https://auth.nuxtjs.org/

On a Nuxt.js project I worked on, I created a sign-out.vue page and emptied the localstorage from there, then redirected to the homepage.

Related

Dynamically add a route in a Nuxt3 middleware

I have a Nuxt3 project where I'd like to add new routes based on an API call to a database. For example, let's say a user navigates to /my-product-1. A route middleware will look into the database and if it finds an entry, it will return that a product page should be rendered (instead of a category page, for example).
This is what I came up with:
export default defineNuxtPlugin(() => {
const router = useRouter()
addRouteMiddleware('routing', async (to) => {
if (to.path == '/my-awesome-product') {
router.addRoute({
component: () => import('/pages/product.vue'),
name: to.path,
path: to.path
})
console.log(router.hasRoute(to.path)) // returns TRUE
}
}, { global: true })
})
To keep it simple, I excluded the API call from this example. The solution above works, but not on initial load of the route. The route is indeed added to the Vue Router (even on the first visit), however, when I go directly to that route, it shows a 404 and only if I don't reload the page on the client does it show the correct page when navigated to it for the second time.
I guess it has something to do with the router not being updated... I found the following example in a GitHub issue, however, I can't get it to work in Nuxt3 as (as far as I'm aware) it doesn't provide the next() method.
When I tried adding router.replace(to.path) below the router.addRoute line, I ended up in an infinite redirect loop.
// from https://github.com/vuejs/vue-router/issues/3660
// You need to trigger a redirect to resolve again so it includes the newly added
route:
let hasAdded = false;
router.beforeEach((to, from, next) => {
if (!hasAdded && to.path === "/route3") {
router.addRoute(
{
path: "/route3",
name: "route3",
component: () => import("#/views/Route3.vue")
}
);
hasAdded = true;
next('/route3');
return;
}
next();
});
How could I fix this issue, please?
Edit:
Based on a suggestion, I tried using navigateTo() as a replacement for the next() method from Vue Router. This, however, also doesn't work on the first navigation to the route.
let dynamicPages: { path: string, type: string }[] = []
export default defineNuxtRouteMiddleware((to, _from) => {
const router = useRouter()
router.addRoute({
path: to.path,
name: to.path,
component: () => import ('/pages/[[dynamic]]/product.vue')
})
if (!dynamicPages.some(route => route.path === to.path)) {
dynamicPages.push({
path: to.path,
type: 'product'
})
return navigateTo(to.fullPath)
}
})
I also came up with this code (which works like I wanted), however, I don't know whether it is the best solution.
export default defineNuxtPlugin(() => {
const router = useRouter()
let routes = []
router.beforeEach(async (to, _from, next) => {
const pageType = await getPageType(to.path) // api call
if (isDynamicPage(pageType)) {
router.addRoute({
path: to.path,
name: to.path,
component: () => import(`/pages/[[dynamic]]/product.vue`),
})
if (!routes.some(route => route.path === to.path)) {
routes.push({
path: to.path,
type: pageType,
})
next(to.fullPath)
return
}
}
next()
})
})
I suggest you use dynamic routing within /page directory structure - https://nuxt.com/docs/guide/directory-structure/pages#dynamic-routes
The [slug] concept is designed exactly for your usecase. You don't need to know all possible routes in advance. You just provide a placeholder and Nuxt will take care of resolving during runtime.
If you insist on resolving method called before each route change, the Nuxt's replacement for next() method you're looking for is navigateTo
https://nuxt.com/docs/api/utils/navigate-to
And I advise you to use route middleware and put your logic into /middleware/routeGuard.global.ts. It will be auto-executed upon every route resolving event. The file will contain:
export default defineNuxtRouteMiddleware((to, from) => {
// your route-resolving logic you wanna perform
if ( /* navigation should happen */ {
return navigateTo( /* your dynamic route */ )
}
// otherwise do nothing - code will flow and given to.path route will be resolved
})
EDIT: However, this would still need content inside /pages directory or some routes created via Vue Router. Because otherwise navigateTo will fail, as there would be no route to go.
Here is an example of one possible approach:
https://stackblitz.com/edit/github-8wz4sj
Based on pageType returned from API Nuxt route guard can dynamically re-route the original URL to a specific slug page.

Make vue router stay on same page if condition is not met

Im working on a vue app and I navigate through pages with vue router from the different page views, I want to set up vue router in a way where it would push a route if there is no error and stay on the same view/route if there is one. I have the following code that succeeds but I'm unsure if that is the best way to do it, and afraid it may cause bugs:
login() {
this.$store
.dispatch("method", {})
.then(() => {
if (!this.error) {
this.$router.push({ name: "nextPage" });
} else {
this.$router.push({ name: "samePage" });
}
});
}

Vue.js Routing shows 404 error message briefly before redirect after auth check

I have followed a number of posts' guidance on Stack Overflow and other websites about setting up Vue routes to check for user authentication. The setup I now have does essentially work correctly (for each route, the user authentication status is checked, and redirected to the login page if necessary) but there is a small issue in that just before the user is redirected, the standard Nuxt/Vue 404 page screen flashes up momentarily.
This is an example of each route that requires user authentication that I have in router.js:
...
{
path: '/question/:id/comments',
name: 'comments',
component: page('Comments.vue'),
meta: {requiresAuth: true},
beforeEnter: (to, from, next) => {
guard(to, from, next);
}
},
...
And here is my guard() function that checks the user authentication and then redirects them with next() as required:
const guard = function(to, from, next) {
axios.get(process.env.apiUrl + 'check-auth').then(response => {
if( to.matched.some( record => record.meta.requiresAdmin ) ) {
if( response.data.is_moderator !== 1 ) {
next({ path: '/' });
} else {
next();
}
} else if( to.matched.some( record => record.meta.requiresAuth ) ) {
if( !response.data.id ) {
next({
path: '/login',
params: { nextUrl: to.fullPath }
});
} else {
next();
}
} else {
next();
}
});
};
Many articles online suggested using localStorage to check the user authentication but this doesn't work in router.js (presumably because its server side and not client side) but I got around this using a Laravel API call with Axios to check it instead.
If anyone can shed any light on why the 404 screen flashes up first before the redirect? Going to the routes directly works fine, so I am guessing it must be something to do with the next() method.

How do I automatically redirect the user if they are already logged in using Vue and Firebase authentication?

Description
I am trying to automatically route the user to the "Games.vue" component if they are already logged in. For authentication I am using Firebase and I check if they are logged in using:
var user = firebase.auth().currentUser;
if (user) {
// User is signed in.
} else {
// No user is signed in.
}
What I want to have happen is for the user to not see the Login page if they are already signed in. So they are taken directly to the Games page. I don't know how to accomplish this using Vue. I need something to run before the Login-component that redirects if logged in.
Attempt at solution
The only way I know how to solve this is to show the Login page, check if the Firebase user is logged in and then go to the Games page. This can work, but it isn't the behavior I am looking for. I am using the Vue router. Thank you for your help.
I would suggest to use a VueRouter global guard like so:
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
if (!user) {
next('login');
} else {
next();
}
})
That being said, you then need a way to specify which route requires authentication. I would suggest to use route meta fields like so:
routes = [
{
name: 'login',
path: '/login',
meta: {
requiresAuth: false
}
},
{
name: 'games',
path: '/games',
meta: {
requiresAuth: true
}
}
]
Now your guards becomes:
if (!user && to.meta.requiresAuth) {
next('login');
} else {
next();
}
Vue router provides an example for this use case, take a look at the documentation.
TIP: Make sure to subscribe to auth changes using Firebase onAuthStateChanged method.
let user = firebase.auth().currentUser;
firebase.auth().onAuthStateChanged(function(user) {
user = user;
});
EDIT: To redirect once logged in, just watch for auth changes and redirect using router.push.
auth.onAuthStateChanged(newUserState => {
user = newUserState;
if (user) {
router.push("/games");
}
});

Vue router allways redirecting to error page

i am trying to setup a redirect when the user is not logged in. But when i do it like in my example the URL changes but i get This page could not be found from nuxt. The code is inside an login.js inside the plugins folder. Then i included this in the nuxt config like this.
plugins: [
'~/plugins/login.js'
],
And here is the actual code for handling redirecting
export default ({ app, store }) => {
app.router.beforeEach((to, from, next) => {
const loggedIn = store.state.account.loggedInAccount
if (!loggedIn) {
if (to.path !== '/redirect') {
next({ path: '/redirect' })
} else {
next()
}
} else {
next()
}
})
}
It looks like the routes are not mounted yet.
You should try to use middleware. It is the conventional way to implement the beforeEach function as mentioned by the official docs. You can read about it from here. If have access to the route object, store object and redirect function inside the middleware, so use redirect to direct to the other routes after validation.