loggedIn state reverts back to false after Logging in and doesn't allow me to guard route - vue.js

I'm trying to guard my routes with state: { loggedIn: false }, when I login from my Login.vue component the goal is to trigger an action this.$store.dispatch('setLogin') that mutates the state of loggedIn to true. There is then navigation guard that is suppose to prevent me form seeing my Login.vue and Regester.vue components. The problem is that it seems like the state changes to true, but not the base state: allowing me to keep hitting the /auth/login and /auth/register routes.
Routes
const routes = [
{
path: '/auth',
name: 'auth',
component: Auth,
children: [
{ name: 'login', path: 'login', component: Login },
{ name: 'register', path: 'register', component: Register },
],
meta: {
requiresVisitor: true,
}
},
{
path: '/logout',
name: 'logout',
component: Logout
}
]
Login Component
login() {
this.$http.get('/sanctum/csrf-cookie').then(response => {
this.$http.post('/login', {
email: this.username,
password: this.password,
}).then(response2 => {
this.$store.dispatch('setLogin')
this.$store.dispatch('getUser')
alert(this.$store.state.loggedIn)
this.$router.push({ name: 'Home' })
}).catch(error => {
console.log(error.response.data);
const key = Object.keys(error.response.data.errors)[0]
this.errorMessage = error.response.data.errors[key][0]
})
});
}
Vuex
export default new Vuex.Store({
state: {
loggedIn: false,
user: JSON.parse(localStorage.getItem('user')) || null,
},
mutations: {
setLogin: (state) => {
state.loggedIn = true
},
SET_USER_DATA (state, userData) {
localStorage.setItem('user', JSON.stringify(userData))
state.user = userData;
},
removeUser(state) {
localStorage.removeItem('user');
state.user = null;
}
},
actions: {
getUser(context) {
if (context.state.loggedIn) {
alert('hit');
return new Promise((resolve, reject) => {
axios.get('api/user')
.then(response => {
context.commit('SET_USER_DATA', response.data.data)
resolve(response)
})
.catch(error => {
reject(error)
})
})
}
},
setLogin(context){
context.commit('setLogin')
}
},
modules: {
}
})
It's strange because alert(this.$store.state.loggedIn) renders true, but when I go back the auth link there's a mounted state alert that comes back false.
Here's my navigation guards as well:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.loggedIn) {
next({
name: 'login',
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresVisitor)) {
if (store.state.loggedIn) {
next({
name: 'Home',
})
return
} else {
next()
}
} else {
next()
}
})

You need to store the loggedIn user in local storage:
setLogin: (state) => {
state.loggedIn = localStorage.setItem('loggedIn', 'true')
state.loggedIn = true
},
Then your state should look like:
state: {
loggedIn: localStorage.getItem('loggedIn') || null,
},

Related

Vue 3, SocketIO - won't join the room after login and router push

I am experiencing the following issue - once the user is logged in, and onMounted event is finished, SocketIO client side should join the room. But this doesn't happen for some reason. I have to manually refresh browser in order to join the room. What am I doing wrong here?
I have the following code for the SocketIO on the client side:
import { io } from "socket.io-client";
const token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
console.log(token);
const ioSocket = io('https://dolphin-app-e4ozn.ondigitalocean.app', {
withCredentials: true,
transportOptions: {
polling: {
extraHeaders: {
'authorization': `${token}`,
},
},
},
});
export const socket = ioSocket
The vue 3 router logic:
import { createRouter, createWebHistory } from 'vue-router'
import Landing from '#/views/Landing.vue'
import Login from '#/views/login/Login.vue'
import ResetPassword from '#/views/login/ResetPassword.vue'
import ForgotPassword from '#/views/login/ForgotPassword.vue'
const routes = [
{
path: '/login',
name: 'login',
component: Login,
meta: {
isGuest: true,
title: 'Servant | Login'
}
},
{
path: '/resetPassword',
name: 'resetPassword',
component: ResetPassword,
meta: {
isGuest: true,
title: 'Servant | Reset Password'
}
},
{
path: '/forgotPassword',
name: 'forgotPassword',
component: ForgotPassword,
meta: {
isGuest: true,
title: 'Servant | Forgot Password'
}
},
{
path: '/',
name: 'landing',
component: Landing,
meta: {
requiresAuth: true,
title: 'Servant',
role: 'waiter'
}
},
{
path: '/:pathMatch(.*)*',
component: Landing
},
]
const router = createRouter({
history: createWebHistory(),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach((to, from, next) => {
document.title = to.meta.title || "Servant"
let token = window.localStorage.getItem('TOKEN') || window.sessionStorage.getItem('TOKEN')
if(to.meta.requiresAuth && !token)
{
next({name: 'login'})
}
if (token && to.meta.isGuest )
{
next({ name: 'landing' })
}
next();
});
export default router
Login component logic:
function login() {
loading.value = true
formClass = ''
if (user.remember)
{
window.localStorage.setItem('remember', user.remember)
}
mainStore
.login(user)
.then((response) => {
loading.value = false
router.push({
name: 'landing',
})
})
.catch((err) => {
loading.value = false
errorMsg.value = err.response.data.messages.error
formClass = 'was-validated'
})
}
Once the component is mounter I have following logic:
onMounted(() => {
socket.emit("join", import.meta.env.VITE_SOCKET_ROOM, (message) => {
console.log(message);
});
})
On the SocketIO server side I have following logic:
io.use((socket, next) => {
const header = socket.handshake.headers["authorization"];
if(header !== 'null')
{
jwtVerify(header, secret).then((res) => {
if (res === true) {
const jwt = jwtDecode(header);
servantID = jwt.payload.iss;
return next();
}
return next(new Error("authentication error"));
});
}
});

Vue and Pinia wont read an updated value from a ref() variable

I'm using Pinia and vue for an auth system that prevents anyone from accesing the private pages, however I would like to change the status of a page from public to private after an initial setup.
{
path: '/settings',
name: 'settings',
component: SettingsView
},
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/setup',
name: 'setup',
component: SetupView,
beforeEnter: (to) => {
const auth = useAuthStore()
console.log(`in router, auth.configInit = ${auth.configInit}`)
if (auth.configInit && to.name !== 'login') {
console.log('entered as true')
return { name: 'login' }
}
}
},
{
path: '/:pathMatch(.*)*',
name: 'page404',
component: Page404View
}]
})
router.beforeEach(async (to) => {
const publicPages = ['/login', '/setup']
const authRequired = !publicPages.includes(to.path)
const auth = useAuthStore()
if (authRequired && !auth.checkSession()) {
auth.returnUrl = to.fullPath
return '/setup'
}
however, whatever I try to do, auth.configInit never changes from false to true. this is the Pinia state:
state: () => ({
token: JSON.parse(localStorage.getItem('userToken')),
sessionTime: JSON.parse(localStorage.getItem('userSession')),
returnUrl: null,
configInit: useStore().store.value.is_config_init
})
and the part that is refering to useStore() is the next one:
const store = ref({
tienda_id: "foo",
is_config_init: false
})
and the proper get under the same class
const get_store_settings = async() => {
const url = `http://${host}/api/store`
await fetch(url, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
})
.then((res) => res.json())
.then((datos) => {
store.value = datos
})
.catch((error) => {
console.log(error)
})
I have very litle experience and I'm the only front end in my workplace, please I need help

vuejs laravel authentication page blocking

what I want to do is to prevent the user from entering the login page again if he is logged in,
Likewise, if the login process is successful, the login page will be blocked.
If the user is not logged in, he cannot go to pages such as home about. Likewise, if the login is successful, it will be possible to return to the login or register page.
app.vue
created() {
this.$store.dispatch("initAuth")
},
<template>
<div>
<h1>login</h1>
<div>
<input type="email" v-model="user.email">
</div>
<div>
<input type="password" v-model="user.password">
</div>
<div>
<button #click="login">Giriş Yap</button>
</div>
</div>
</template>
<script>
export default {
name: "Login",
data() {
return {
user: {
email: null,
password: null,
},
isUser: false
}
},
methods: {
login() {
this.$store.dispatch("login", {...this.user, isUser: this.isUser})
}
}
}
</script>
router.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import store from '../store'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
component: () => import('#/views/Home.vue'),
meta: {
requiresAuth: true
},
},
{
path: '/about',
name: 'about',
component: () => import('#/views/About.vue'),
meta: {
requiresAuth: true,
},
},
{
path: '/login',
name: 'login',
component: () => import('#/views/Login.vue'),
},
{
path: '/register',
name: 'register',
component: () => import('#/views/Register.vue'),
},
{
path: '/error-404',
name: 'error-404',
component: () => import('#/views/error/Error404.vue'),
},
{
path: '*',
redirect: 'error-404',
},
],
})
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isAuthenticated) {
next({name: 'login'})
} else {
next()
}
} else {
next() // make sure to always call next()!
}
});
export default router
import Vue from 'vue'
import Vuex from 'vuex'
import axios from "axios";
import router from '../router';
Vue.use(Vuex)
export default new Vuex.Store({
state: {
token: "",
},
mutations: {
setToken(state, token) {
state.token = token
},
clearToken(state) {
state.token = ""
}
},
actions: {
initAuth({commit, dispatch}) {
let token = localStorage.getItem("token")
if (token) {
commit('setToken', token)
} else {
router.push('/login')
commit('clearToken')
//return false;
}
},
login({commit, dispatch, state}, autData) {
return axios.post(
'/api/login', {
email: autData.email,
password: autData.password
})
.then(response => {
commit('setToken', response.data.token)
localStorage.setItem('token', response.data.token)
router.push('/about')
console.log(response)
})
.catch(error => {
console.log(error)
})
},
register({commit, dispatch, state}, autData) {
return axios.post(
'/api/register', {
name: autData.name,
email: autData.email,
password: autData.password,
password_confirmation: autData.password_confirmation
})
.then(response => {
router.push('/about')
commit('setToken', response.data.token)
console.log(response)
})
.catch(error => {
console.log(error.response.data.errors);
})
},
logout({commit}) {
commit('clearToken')
localStorage.removeItem('token')
router.push('/');
},
setTimeoutTimer({dispatch}, expiresIn) {
setTimeout(() => {
dispatch("logout")
}, expiresIn)
}
},
getters: {
isAuthenticated(state) {
return state.token !== ""
}
},
modules: {},
})
add a new meta key requiresNotAuth for login and register routes then change beforeEach content like this
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.getters.isAuthenticated) {
next({name: 'login'})
} else {
next()
}
}
else if (to.matched.some(record => record.meta.requiresNotAuth)) {
if (store.getters.isAuthenticated) {
next({name: 'home'})
}
else {
next()
}
}
else {
next() // make sure to always call next()!
}

Vue Router Warning and Router.push is not Functioning

I am trying to route to another page after getting response from adonis project. Calling to post method is working. However router.push('/') is not functioning. Only login page reloaded every time when I submitted the b-from.
async login({ commit, state }) {
console.log("Login")
try {
const response = await HTTP()
.post('/admin/login', {
email: state.loginEmail,
password: state.loginPassword
})
.then(response => {
console.log("Ok")
console.log(response.data)
if (response.data == 'UserNotFoundException') {
alert('User not found')
router.push('/')
}
if (response.data == 'PasswordMisMatchException') {
alert('password not ms')
router.push('/')
}
if (response.data.token) {
console.log(response)
//commit('setToken', response.data.token)
router.push('/')
} else {
router.push('/')
}
})
console.log(response)
//return router.push('/')
} catch (error) {
console.log(error)
}
},
Routes:
routes: [
{
name: "FullLogin",
path: "/login",
component: () => import("#/views/authentication/FullLogin"),
},
{
path: "/",
redirect: "/dashboard/docs-dashboard",
component: () => import("#/layouts/full-layout/FullLayout"),
children: [
{
name: "Dashboard",
path: "/dashboard/docs-dashboard",
component: () => import("#/views/dashboards/docsDashboard"),
},
]
}
]
router.beforeEach((to, from, next) => {
next()
})
I can't figure out why router.push('/') is not routing.
According to docs:
Note: Inside of a Vue instance, you have access to the router instance as $router. You can therefore call this.$router.push.

How to solve the error "Redirected when going from ' / ' to '/dashboard' via a navigation guard"?

When I login with a user, it redirects me to the dashboard as expected. As soon as I logout and try to login again (even with another user, and WITHOUT refreshing the page) it gives me back this error in console:
I just want to redirect the user in the dashboard if authenticated, even when the page is not refreshed cause I did notice that if I refresh the page I can login without problems.
Help me if you can. Down here some code:
Login method
methods: {
...mapActions({
attempt: "auth/attempt",
}),
submit(credentials) {
axios
.post("http://127.0.0.1:8000/api/login", credentials)
.then((res) => {
// console.log(res.data);
if (res.data.success) {
this.attempt(res.data.token)
}
if (res.data.errors) {
this.loginErrors = res.data.errors;
} else {
this.$router.push({ name: 'dashboard' })
}
})
.catch((err) => {
if (
err.name !== "NavigationDuplicated" &&
!err.message.includes(
"Avoided redundant navigation to current location"
)
) {
console.log(err);
}
});
},
},
dashboard path in the router
{
path: '/dashboard',
name: 'dashboard',
component: DashboardComponent,
beforeEnter: (to, from, next) => {
if (!store.getters['auth/authenticated']) {
return next({
name: 'home'
})
}
next()
}
},
attempt action in vuex store
async attempt({ commit, state }, token) {
if (token) {
commit('SET_TOKEN', token)
}
// se non c'è
if(!state.token) {
return
}
try {
await axios.get('http://127.0.0.1:8000/api/user')
.then(res => {
commit('SET_USER', res.data)
})
} catch (e) {
commit('SET_TOKEN', null)
commit('SET_USER', null)
}
},
others from vuex
namespaced: true,
state: {
token: null,
form: null,
},
getters: {
authenticated(state) {
return state.token && state.form
},
user(state) {
return state.form
},
},
mutations: {
SET_TOKEN(state, token) {
state.token = token
},
SET_USER(state, data) {
state.form = data
},
},
Update: the call to attempt() should be awaited, otherwise this.$router.push({ name: 'dashboard' }) (and therefore the guard function on the /dashboard route) will be called before the call to the /api/user API has completed:
submit(credentials) {
axios
.post("http://127.0.0.1:8000/api/login", credentials)
.then(async (res) => {
// console.log(res.data);
if (res.data.success) {
await this.attempt(res.data.token)
}
if (res.data.errors) {
this.loginErrors = res.data.errors;
} else {
this.$router.push({ name: 'dashboard' })
}
})
.catch((err) => {
if (
err.name !== "NavigationDuplicated" &&
!err.message.includes(
"Avoided redundant navigation to current location"
)
) {
console.log(err);
}
});
},
next is a function that should be called exactly once (not returned).
Try changing the code in the router to:
{
path: '/dashboard',
name: 'dashboard',
component: DashboardComponent,
beforeEnter: (to, from, next) => {
if (!store.getters['auth/authenticated']) {
next({ name: 'home' })
} else {
next()
}
}
},