guys. Can someone help me to fix this problem with redirect?
I have this router file:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!store.state.auth.is_authenticated) {
next({
path: '/login',
query: {
redirect: to.fullPath
}
})
} else {
next()
}
} else if (to.matched.some(record => record.meta.requiresGuest)) {
if (store.state.auth.is_authenticated) {
next({
path: '/',
query: {
redirect: to.fullPath
}
})
} else {
next()
}
} else {
next()
}
})
Current auth status is stored in vuex state. When I`m logging in status is changing but nothing is happening with page, only Header component updates.
<script>
import { mapGetters, mapActions } from "vuex";
export default {
name: "Login",
data: () => {
return {
email: "",
password: ""
};
},
computed: mapGetters(["currentUser"]),
methods: {
...mapActions(["loginUser"]),
login: function(event) {
event.preventDefault();
this.loginUser({ email: this.email, password: this.password });
}
},
updated() {
if (this.currentUser.is_authenticated) {
this.$router.go({ path: this.$router.path });
}
}
};
</script>
Please modify your code like this and check
helper.js
export function initialize(store, router)
{ router.beforeEach((to,from,next)=>{
const requiresAuth= to.matched.some(record=>record.meta.requiresAuth)
const currentUser = store.state.auth.user;
if(requiresAuth && !currentUser){ next('/login');}
else if(to.path=='/login' && currentUser){ next('/')}
//if user already authenticated and user tries to open login page
//redirect to / path , it can be modified as per config
else { next()}
})
}
Add this in your main.js
import {initialize} from './helper';
initialize(store, router);
In you login page updated part not required as router.beforeEach code in helper.js will be able to take care of that.
methods: {
...mapActions(["loginUser"]),
login: function(event) {
event.preventDefault();
this.loginUser({ email: this.email, password: this.password })
.then(()=>{ this.$router.replace({name:'dashboard'})
//forward to this path after login
}).catch(()=>{console.log('login fail') });
}
},
If store.state.auth.is_authenticated is true means that you logged in you must do after that flag go true something like this this.$router.go(); if you make this with no argument thats means that you will go in exact same page that you are (login page) but then it will triggered by your guards and it will redirect you to panel page or so
Related
Hi everybody i'm trying to make login page and redirect to home page ('/')
When i'm logging i haven't errors in console i can see the error using vue devtools
ERROR VUE DEV TOOL
End of navigation
/login
02:27:19.124
guard:afterEach
failure:Avoided redundant navigation to current location: "/login".
status:❌
from:/login
fullPath:"/login"
path:"/login"
query:Object (empty)
hash:""
name:"login"
params:Object (empty)
matched:Array[1]
meta:Object (empty)
redirectedFrom:undefined
href:"/login"
to:/login
fullPath:"/login"
hash:""
query:Object (empty)
name:"login"
path:"/login"
params:Object (empty)
matched:Array[1]
meta:Object (empty)
redirectedFrom:Object
href:"/login"
this is my login's method
methods:{
async submitForm(user){
const userForm=new FormData();
userForm.append("username", this.username);
userForm.append("password", this.password);
await this.$store.dispatch("auth/login", userForm).then(
()=>{
const user = localStorage.getItem('user')
console.log(user) //to check if i logged, in console get undefined but if try localStorage.getItem('user') i got the user.
this.$router.push('/')
}),
(error)=>{
console.log(error)
}
}
}
ROUTES
const router = createRouter({
history: createWebHistory(),
scrollBehavior() {
return { top: 0 }
},
routes,
})
{
path: '/',
name: 'dashboard',
component: () => import('#/views/Dashboard.vue'),
children: [
{
path: '',
name: 'home',
component: () => import('#/views/dashboard/Home.vue'),
},
....
router.beforeEach((to, from, next) => {
const publicPages = ['/login'];
const authRequired = !publicPages.includes(to.path);
const loggedIn = localStorage.getItem('user');
// trying to access a restricted page + not logged in
// redirect to login page
if (authRequired && !loggedIn) {
next('/login');
} else {
next();
}
});
auth.service
class AuthService {
login(user) {
let dator={
access_token: '',
user:{}
}
console.log('AUTHSERVICE-->\n'+user)
return axios
.post(API_URL + 'login/access-token', user)
.then(response => {
console.log(response.data.access_token)
if (response.data.access_token) {
dator.access_token=response.data.access_token
localStorage.setItem('token', JSON.stringify(dator.access_token))
axios.get(API_URL + 'users/me/', { headers: authHeader() })
.then(response =>{
localStorage.setItem('user', JSON.stringify(response.data))
dator.user=response.data
})
}
return dator;
});
}
auth.module VUEX
import AuthService from '../services/auth.service';
const token = JSON.parse(localStorage.getItem('token'));
const user = JSON.parse(localStorage.getItem('user'));
const initialState = token && user
? { status: { loggedIn: true }, token,user }
: { status: { loggedIn: false }, token:null, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, userForm) {
console.log(userForm)
return AuthService.login(userForm).then(
datologin => {
console.log('datologin',datologin)
commit('loginSuccess', datologin);
return Promise.resolve(datologin);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
if after login i force the '/' in the browser the page work. So i don't know where is my bad.
Sorry for noob error. The problem was in second request with axios so i refactor this part and now work
async asyncLogin(user){
let uservuex={
access_token: '',
user:{}
}
try{
const token = await axios.post(API_URL + 'login/access-token', user)
uservuex.access_token = await token.data.access_token
localStorage.setItem('token', JSON.stringify(uservuex.access_token))
const me = await axios.get(API_URL + 'users/me/', { headers: authHeader() })
uservuex.user= await me.data
localStorage.setItem('user', JSON.stringify(uservuex.user))
}catch(error){
console.log(error)
}
return uservuex
}
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()
}
}
},
I added middlewares to default.vue component:
export default {
components: {
TheHeader
},
middleware: ['auth'],
}
My auth.js:
export default function ({ app, store }) {
if (app.$cookies.get('AUTH_TOKEN')) {
var AUTH_TOKEN = app.$cookies.get('AUTH_TOKEN')
app.$axios.$post('https://example.com/api', {
email: Buffer.from(AUTH_TOKEN[0], 'base64').toString(),
password: Buffer.from(AUTH_TOKEN[1], 'base64').toString(),
}).then(response => {
store.dispatch('changeAuthStatus', {
authStatus: true,
userData: {
id: response.data.id,
login: response.data.login,
email: response.data.email,
firstName: response.data.first_name,
lastName: response.data.last_name,
}
})
})
}
}
So I can't understand why my middlewares don't load when the page is reloaded or with direct access to the page. Also mode: 'universal' and ssr: true are set in nuxt.config.js
Here is the documentation for the middlewares: https://nuxtjs.org/docs/2.x/directory-structure/middleware/
Few steps to have a working middleware:
use it only in a page (/pages/hello.vue) or layout (/layouts/MyFancyLayout.vue)
put the middleware in the proper directory (/middleware/test.js)
call it properly in the .vue file like middleware: 'test'
You can also try to debug and see if something like this works
export default {
middleware() {
console.log('working!')
}
}
It is working on client transitions and should be good on initial page load aswell.
As a more accurate way, you should do this in the Vuex Store using the 'context' attribute.
middleware/auth.js
export default function(context) {
if (process.client) {
context.store.dispatch('initAuth', null);
}
context.store.dispatch('initAuth', context.req);
}
store/index.js
import Cookie from 'js-cookie'
store/index.js
actions:{
initAuth(vuexContext,state){
let token;
let jwtCookie;
if (req) {
if (!req.headers.cookie) {
return;
}
jwtCookie = req.headers.cookie
.split(";")
.find(c => c.trim().startsWith("jwt="));
if (!jwtCookie) {
return;
}
token = jwtCookie.split('=')[1];
} else {
token = localStorage.getItem('token');
}
vuexContext.commit('setToken', token);
return $axios.post('https://example.com/api', {
email: Buffer.from(AUTH_TOKEN[0], 'base64').toString(),
password: Buffer.from(AUTH_TOKEN[1], 'base64').toString(),
}).then(response => {
vuexContext.commit('changeAuthStatus', {
authStatus: true,
userData: {
id: response.data.id,
login: response.data.login,
email: response.data.email,
firstName: response.data.first_name,
lastName: response.data.last_name,
}
})
})
}
}
this way it will work smoothly and understandably
How can I manage the url for front and admin panel Via Middleware in Vue.
This is the code I have written in router/index.js file:
const router = new VueRouter({ mode: 'history', routes });
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const loggedIn = localStorage.getItem('usertoken') == null ? false : true;
const user = JSON.parse(localStorage.getItem('user'));
//this is for admin
next('/admin/login')
next('/admin/home');
//this is my front URL
next('/terms-condition');
next('/home');
next()
})
export default router;
See the below code it may helps you
/**
* middleware for authentication
*/
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const loggedIn = localStorage.getItem('usertoken') == null ? false : true;
const user = JSON.parse(localStorage.getItem('user'));
if (to.meta.portal == 'admin') {
if (to.meta.auth) {
if (!loggedIn) {
next('/admin/login')
} else if (loggedIn) {
next();
}
} else {
if (!loggedIn) {
next();
} else if (loggedIn) {
if (user.role_id == '1') {
next('/admin/home');
} else {
next('/');
}
}
}
} else if (to.meta.portal == 'front') {
if (loggedIn) {
if (user.role_id == '1') {
next('/admin/home');
} else {
next('/');
}
} else if (!loggedIn) {
if (to.path == "/admin") {
next('/admin/login');
} else {
next();
}
}
}
next()
})
export default router;
And you need to create two router files one for front and other for admin:
//front route file will look like
export default [{
path: '/',
meta: { auth: false, portal: 'front' },
component: () => import('#/components/layouts/front/main.vue'),
children: [
{
path: '/',
name: 'front-home',
title: 'Dashboard',
meta: { auth: false, portal: 'front' },
}
]
}]
//admin router file will be like
export default [
{
path: 'user',
name: 'users',
title: 'Users',
meta: { auth: true, portal: 'admin' },
component: () => import('#/components/templates/admin/user'),
}
]
Main difference is the portal that defines which portal will access by the respective route.Without portal inside meta it won't work.
The way you have implemented is correct
Once the user is successfully logged in , use if else condition to redirect to admin panel, also use Navigation guards given in vue-router
https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard
This help to prevent the other user to use this url directly
I want to redirect user to home page if logged-in and wants to access login/register page and redirect user to login page if not logged-in and wants to access other pages. Some pages are excluded that means there is no need to be logged in so my code is right below:
router.beforeEach((to, from, next) => {
if(
to.name == 'About' ||
to.name == 'ContactUs' ||
to.name == 'Terms'
)
{
next();
}
else
{
axios.get('/already-loggedin')
.then(response => {
// response.data is true or false
if(response.data)
{
if(to.name == 'Login' || to.name == 'Register')
{
next({name: 'Home'});
}
else
{
next();
}
}
else
{
next({name: 'Login'});
}
})
.catch(error => {
// console.log(error);
});
}
});
But the problem is that it gets into an infinite loop and for example each time login page loads and user not logged-in, it redirects user to login page and again to login page and...
How can I fix this?
Here's what I'm doing. First I'm using a meta data for the routes, so I don't need to manually put all routes that are not requiring login:
routes: [
{
name: 'About' // + path, component, etc
},
{
name: 'Dashboard', // + path, component, etc
meta: {
requiresAuth: true
}
}
]
Then, I have a global guard like this:
router.beforeEach((to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
// this route requires auth, check if logged in
// if not, redirect to login page.
if (!store.getters.isLoggedIn) {
next({ name: 'Login' })
} else {
next() // go to wherever I'm going
}
} else {
next() // does not require auth, make sure to always call next()!
}
})
Here I am storing if the user is logged in or not, and not making a new request.
In your example, you have forgotten to include Login into the list of pages that "don't need authentication". So if the user is trying to go to let's say Dashboard, you make the request, turns out he's not logged in. Then he goes to Login, BUT your code checks, sees it's not part of the 3 "auth not required" list, and makes another call :)
Therefore skipping this "list" is crucial! ;)
Good luck!
If someone is still looking for an answer, you can reverse the logic. So, the same way you have requiresAuth, you will have hidden routes for authenticated users. (example with firebase)
routes: [{
path: '/',
redirect: '/login',
meta: {
hideForAuth: true
}
},
{
path: '/dashboard',
name: 'dashboard',
component: Dashboard,
meta: {
requiresAuth: true
}
}]
And in your beforeEeach
router.beforeEach((to, from, next) => {
firebase.auth().onAuthStateChanged(function(user) {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!user) {
next({ path: '/login' });
} else {
next();
}
} else {
next();
}
if (to.matched.some(record => record.meta.hideForAuth)) {
if (user) {
next({ path: '/dashboard' });
} else {
next();
}
} else {
next();
}
});
});
Derived from the answer #Andrey Popov provided.
I prefer to explicitly disallow a route that doesn't require auth. This prevents accidentally not protecting your routes, i.e. the default case is to redirect to a login page
router.beforeEach((to, from, next) => {
if (to.name === 'login') {
next() // login route is always okay (we could use the requires auth flag below). prevent a redirect loop
} else if (to.meta && to.meta.requiresAuth === false) {
next() // requires auth is explicitly set to false
} else if (store.getters.isLoggedIn) {
next() // i'm logged in. carry on
} else {
next({ name: 'login' }) // always put your redirect as the default case
}
})
In addition to the Andrey's answer, if you use firebase authentication, need to add onAuthStateChanged around createApp in main.ts.
firebase.auth().onAuthStateChanged((user) => {
createApp(App).use(store).use(router).mount('#app')
})
This is very basic concept for this, use redirect:'/dashboard' this way you can do. you have to define it in your route list. like this way. you can ignore mate: {}. i used this for different purpose.
routes: [ ....
{
path: '/login',
name: 'login',
component: LoginView,
meta:{needAuth:false},
redirect:'/dashboard'
},
{
path: '/sign-up',
name: 'sign-up',
component: SignUpView,
meta:{needAuth:false},
redirect:'/dashboard'
},
{
path: '/dashboard',
name: 'dashboard',
component: UserDashboard,
meta:{needAuth:true},
beforeEnter: ifAuthenticated,
}
]
function ifAuthenticated (to, from, next) { store.test();
if (localStorage.getItem("login-token")) { console.log("login done");
next();
return;
}
router.push({ name: 'login' });
};
// requireAuth for those you want to authenticate
// onlyGuest for those you don't want to autenticate to open other thing if already
// logged in then it's redirect to home
router.beforeEach((to,from,next)=>{
if(to.matched.some(record => record.meta.requireAuth)){
if(!store.state.loginUser){
next({name:'Login'});
}
else{
next();
}
}
else if (to.matched.some(record => record.meta.onlyGuest)) {
if (store.state.loginUser && store.state.loginUser.token) {
next({name: "Home"});
} else {
next();
}
}
else{
next();
}
});