Vue: How to use Per-Route Guard with Vuex getter? - javascript

Similar to the way that we handle with isAuthenticate function to check if user has properly authenticated, I'm trying to inspect in my store.
const state = {
cliente: []
};
const getters = {
//Verificar Regra
CHECK_CLIENTE_STATE: (state) => {
return state.cliente
}
}
const actions = {
FETCH_DADOS({ commit }, obj) {
return fetch(`http://localhost:3030/pessoas/informacao/${obj['data']}`)
.then(response => response.json())
.then(data => commit('SetCliente', data))
.catch(error => console.log(`Fetch: ${error}`))
}
}
const mutations = {
SetCliente(state, cliente) {
state.cliente = cliente
}
}
login page,
methods:{
fetch(){
this.$store.dispatch("FETCH_DADOS",{'data':'12345'})
this.$router.push('/')
}
}
At the first fetch click, I inspect Vuex, apparently it is working.
Routes:
const routes = [{
path: '/',
beforeEnter: (to, from, next) => {
if (store.getters.CHECK_CLIENTE_STATE == '') {
next('/login')
}
next();
},
component: () =>
import ('../views/Home')
},
{
path: '/login',
component: () =>
import ('../views/Login')
}
]
Well, in console.log at the first fetch click, I receive this error, but in vuex as shown above, the store is filled.
Uncaught (in promise) Error: Redirected when going from "/login" to
"/" via a navigation guard.
Why just in the second click is it redirected to home, not in the first?
Updating
Trying a new approach in router.js
path: '/',
beforeEnter: (to, from, next) => {
console.log(!store.getters.CHECK_CLIENTE_STATE.length)
if (!store.getters.CHECK_CLIENTE_STATE.length) {
next('/login')
}
next();
},
component: () =>
import ('../views/Home')
But again, the first fetch is TRUE and the second FALSE, in the second I'm redirected to /home

The router is being directed before the data is loaded. Wait for it:
methods:{
async fetch(){
await this.$store.dispatch("FETCH_DADOS",{'data':'12345'})
this.$router.push('/')
}
}

Related

$router.push() not a function error using Vuex

After a new project is created, I'd like to route the user to another page so they can add more information to the project.
This is working:
createProject() {
ProjectService.createProject(this.project)
.then(response => {
this.$router.push({
name: "project-update",
params: { id: response.data.data.id }
});
})
}
I'd like to use vuex to handle all this, but this is not working.
createProject() {
this.$store
.dispatch("project/postProject", this.project)
.then(response => {
this.$router.push({
name: "project-update",
params: { id: response.data.data.id }
});
})
.catch(() => {});
}
The error I'm getting is: "state.projects.push is not a function"
This is my postProject action in Vuex:
postProject({ commit, dispatch }, project) {
return ProjectService.createProject(project)
.then(() => {
commit('ADD_PROJECT', project);
const notification = {
type: 'success',
message: 'Your project has been created!'
};
dispatch('notification/add', notification, { root: true });
})
.catch(error => {
const notification = {
type: 'error',
message: 'There was a problem creating your project: ' + error.message
};
dispatch('notification/add', notification, { root: true });
throw error;
});
}
Looks like the context of 'this' is not reaching the router or the push function therein. How can I access the router and route to that next page?
What you can do is import your router module into your vuex module like so:
import {router} from "../main.js"
// or
import router from '../router'
export default {
actions: {
createProject () {
this.$store
.dispatch("project/postProject", this.project)
.then(response => {
router.push({
name: "project-update",
params: { id: response.data.data.id }
})
})
.catch(() => { })
}
}
}
I have a same issue but I solved by doing this:
this.$router.replace("/");
Having issue in vuex and nuxt store by using this : this.$router.push("/");

How to set authorization header and protect routes in Vuejs and Node

I am using passport-jwt strategy to protect auth users in my app, once I login I am generating a jwt-token now I want to protect my welcome page rout so that user cannot open it without login
So when I login I am creating jwt-token with payload like this
my user.js file
const payload = { email: rows[0].email } // jwy payload
console.log('PAYLOAD')
console.log(payload)
jwt.sign(
payload,
key.secretOrKey, { expiresIn: 3600 },
(err, token) => {
res.json({
success: true,
token: 'Bearer ' + token,
email
})
})
Now in my passport.js I am doing like this
const opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
opts.secretOrKey = keys.secretOrKey;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
let payLoadEmail = jwt_payload.email //payload data what I have passed in creating jwt
console.log("payload email :" + payLoadEmail)
User.fetchLogedInUser(payLoadEmail)
.then(([rows]) => {
if (rows.length > 0) {
return done(null, rows[0].email) // returning data what I need
}
return done(null, false)
})
.catch(err => console.log(err))
}));
Both are working fine.
Now I want to protect my welcome rout so in my router.js file
const express = require('express');
const router = express.Router();
const passport = require('passport')
const UsersCtrl = require('../controllers/users');
router.use('/login', UsersCtrl.login)
router.use('/welcome',passport.authenticate('jwt',{session:false}))
router.use('/logout', UsersCtrl.logout)
module.exports = router;
suppose user types localhost:8080/welcome without login then I want to protect it
So in my store.js file when user logs in I am doing this on login click and I have made a method getAuthUser. I don't know how to I pass this config to protect my welcome file
Here is my full store.js code
import axios from 'axios'
import jwt from 'jsonwebtoken'
function checkTokenValidity(token) { // token validity
if (token) {
const decodedToken = jwt.decode(token)
return decodedToken && (decodedToken.exp * 1000) > new Date().getTime()
}
return false
}
export default {
namespaced: true,
state: {
user: null,
isAuthResolved: false // this I am calling on my login page i am confused where should I call this or not to call this
},
getters: {
authUser(state) {
return state.user
},
isAuthenticated(state) {
return !!state.user
}
},
actions: {
loginWithCredentials({ commit }, userDate) {
return axios.post('/api/v1/users/login', userDate)
.then(res => {
const user = res.data
console.log(user.email)
localStorage.setItem('jwt-token', user.token)
commit('setAuthUser', user)
})
},
logout({ commit }) {
return new Promise((resolve, reject) => {
localStorage.removeItem('jwt-token')
commit('setAuthUser', null)
resolve(true)
})
},
getAuthUser({ commit, getters }) {
const authUser = getters['authUser']
const token = localStorage.getItem('jwt-token')
const isTokenValid = checkTokenValidity(token)
if (authUser && isTokenValid) {
return Promise.resolve(authUser)
}
const config = { // here what to do with this how can I pass this to protect my route
headers: {
'cache-control': 'no-cache',
'Authorization': token
}
}
}
},
mutations: {
setAuthUser(state, user) {
return state.user = user
},
setAuthState(state, authState) {
return state.isAuthResolved = authState
}
}
In my route.js vue file
import Vue from 'vue'
import Router from 'vue-router'
import store from './store'
Vue.use(Router)
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [{
path: '/welcome',
name: 'welcome',
meta: { onlyAuthUser: true },
component: () =>
import ('./views/Welcome.vue'),
}, ]
})
router.beforeEach((to, from, next) => {
store.dispatch('auth/getAuthUser')
.then((authUser) => {
const isAuthenticated = store.getters['auth/isAuthenticated']
if (to.meta.onlyAuthUser) {
if (isAuthenticated) {
next()
} else {
next({ name: 'PageNotAuthenticated' })
}
} else if (to.meta.onlyGuestUser) {
if (isAuthenticated) {
next({ name: 'welcome' })
} else {
next()
}
} else {
next()
}
})
})
export default router
My main problem is I want to protect routes and make the user authenticated using jwt and passport I am getting jwt once I login and want to check once my protected rout is access with out login for backend.
In front end (vue.js) I my store file in action> getAuthUsers I don't know how to pass config to other routes like my welcome.
Not sure if I understood your question entirely because you seem to be implementing route access correctly. You simply need to add routes as an array while the rest of your code remains the same.
const router = new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [{
path: '/welcome',
name: 'welcome',
meta: { onlyAuthUser: true },
component: () =>
import ('./views/Welcome.vue'),
},
{
path: '/login',
name: 'Login',
meta: { onlyGuesUser: true },
component: () =>
import ('./views/Login.vue'),
}]
})
For using Authentication: Bearer xxxxxxxx you can modify your axios code to directly use required headers instead of passing it through routes every time. Make a new folder called services and a file called base-api. You can obviously name it however you like, but this is my setup.
import axios from 'axios';
export default () => {
let headers = {
'cache-control': 'no-cache'
};
let accessToken = localStorage.getItem('jwt-token');
if (accessToken && accessToken !== '') {
headers.Authorization = accessToken;
};
return axios.create({
baseURL: 'SECRET_URL_',
headers: headers
});
}
Import this file in your store.js. Replace import axios from 'axios' with import axios from '#src/services/base-api.js. As the file is returning an axios instance you need to access it as axios(). Which means you function would be
return axios().post('/api/v1/users/login', userDate)
.then(res => {
// do whatever
})

Vue - Mixin on page refresh

When I navigate using vue router, everything works. Ex: I click on button using #click=login(). Above the login method
methods: {
login () {
this.$router.push('/admin')
}
So vue redirect user to '/Admin' page and the router calls beforeEach event where I check if the page has meta attribute to validate user access
Then on my router/index.js
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
Routes
]
})
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAdminAuth) {
let app = router.app.$data || {isAuthenticated: false}
if (app.isAuthenticated) {
// already signed in, we can navigate anywhere
next()
} else if (to.matched.some(record => record.meta.requiresAdminAuth)) {
// authentication is required. Trigger the sign in process, including the return URI
console.log(router.app.hello)
let authenticate = router.app.authenticate
authenticate(to.path).then(() => {
console.log('authenticating a protected url:' + to.path)
next()
})
} else {
// No auth required. We can navigate
next()
}
} else {
next()
}
})
To validade the user I'm usgin global method and this works. But when user navigate to '/Admin' from URL, not from my button, the global variable is undefined. So I tried to use Mixin, but the result was the same.
Here is my main.js where I specify global method and the mixin:
var myMixin = {
methods: {
hello: function () {
console.log('Bem-vindo ao mixin!')
}
}
}
const globalData = {
isAuthenticated: false,
user: '',
mgr: mgr
}
const globalMethods = {
async authenticate (returnPath) {
const user = await this.$root.getUser()
if (user) {
this.isAuthenticated = true
this.user = user
} else {
await this.$root.signIn(returnPath)
}
},
async getUser () {
try {
let user = await this.mgr.getUser()
console.log(user)
return user
} catch (err) {
console.log(err)
}
},
signIn (returnPath) {
returnPath ? this.mgr.signinRedirect({ state: returnPath })
: this.mgr.signinRedirect()
}
}
/* eslint-disable no-new */
new Vue({
mixins: [myMixin],
el: '#app',
router,
store,
template: '<App/>',
components: { App },
data: globalData,
methods: globalMethods
})
How can I do that? Execute a defined method when user navigate by URL?

Authentication work in the not appropriate way Vue js

I have guarded routes, and they depend on the value in the store.
loginUser() { // i call this function when user press "Login"
this.$http.get('http://localhost:3000/users')
.then(resp => {
return resp.json()
})
.then(resp => resp.filter(item => item.email === this.email && item.password === this.password))
.then(res => res.length > 0 ? (this.$router.push('/'), this.$store.commit('logUser')) : this.visible = true); // here i change value in the store
}
// Store
userLogged: false // state
mutations: {
logUser(state) { // i call this function when loginUser() function calls
state.userLogged = true
}
}
// router index.js
{
path: '/',
name: 'Home',
component: Home,
beforeEnter: AuthGuard
}
export default function (to, from, next) { // this is AuthGuard function
if (store.getters.getUser) { // get value from store
next();
} else {
next('/login')
}
}
// store
getters: { // get value
getUser(state) {
return state.userLogged
}
}
This works in the not appropriate way. I have to click the login button twice because getUser() get the state and logUser() changing userLogged simultaneously i.e. getUser() return false and when i am clicking second time its true. How can i fix this?

How to stop component-loading and redirect in Vue?

I have a component which is not to be accessed by non-logged-in user. I implemented this logic into the beforeCreate hook. Problem is that this doesn't stop the component from continuing in loading, which I want it to.
This is my code:
<script>
export default {
beforeCreate: function () {
if (this.$root.auth.user === null) {
this.$router.push({ name: 'auth.login' })
}
},
mounted: function () {
// some code that SHOULD NOT be processed
// if the user isn't authenticated
}
}
</script>
What am I doing wrong?
You should move your beforeCreate function to the router itself.
Here's my Auth catch
router.beforeEach(
(to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// if route requires auth and user isn't authenticated
if (!store.state.Authentication.authenticated) {
let query = to.fullPath.match(/^\/$/) ? {} : { redirect: to.fullPath }
next(
{
path: '/login',
query: query
}
)
return
}
}
next()
}
)
It allows me to use my routes definition to handle auth and guest placements.
{
path: '/',
component: load('Template'),
children: [
{ path: '', component: load('Dashboard'), name: 'Dashboard' }
],
meta: { requiresAuth: true }
},
{
path: '/login',
component: load('Authentication/Login'),
name: 'login'
},
By having it in the router it's called before the components are initialized by Vue, this will stop processing of any component level events.

Categories