Wat is het probleem?

Sommige pagina's mogen alleen zichtbaar zijn voor ingelogde gebruikers — bijvoorbeeld /dashboard of /profile. Als een uitgelogde gebruiker daar naartoe gaat, wil je hem terugsturen naar de login.

Vue Router heeft hiervoor navigation guards: code die draait voordat de gebruiker naar een nieuwe pagina gaat.

Vooraf: deze pagina gebruikt de Auth Store en Vue Router. Zorg dat die staan.

Frontend beveiliging is niet écht veilig. Een slimme gebruiker kan je JS aanpassen. De échte beveiliging zit altijd op de server (API checkt het token). Navigation guards zijn voor de gebruikerservaring — niet de echte security.

Routes markeren als beveiligd

In je route-config voeg je meta: { requiresAuth: true } toe aan de routes die alleen voor ingelogde users zijn:

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import LoginView from '../views/LoginView.vue'
import DashboardView from '../views/DashboardView.vue'
import ProfileView from '../views/ProfileView.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: HomeView },
    { path: '/login', component: LoginView },

    // Beveiligd
    {
      path: '/dashboard',
      component: DashboardView,
      meta: { requiresAuth: true }
    },
    {
      path: '/profile',
      component: ProfileView,
      meta: { requiresAuth: true }
    }
  ]
})

export default router

Waarom met meta? Zo houd je de check op één plek (de guard) en hoef je per route alleen aan te geven of hij beveiligd is. Veel cleaner dan een lijst hard-coden.

De Navigation Guard

Voeg vlak voor export default router een beforeEach toe. Die draait voor elke navigatie:

// src/router/index.js
import { useAuthStore } from '@/stores/auth'

// ...routes...

router.beforeEach((to) => {
  const auth = useAuthStore()

  // Heeft deze route auth nodig EN is user niet ingelogd?
  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return '/login'   // → omleiden naar login
  }

  // Anders: gewoon doorlaten
})

export default router

Hoe werkt beforeEach?

  • Draait voor elke navigatie in je app
  • to = de route waar de gebruiker naartoe gaat
  • Return een pad-string → omleiden naar die route
  • Return niks of true → gewoon doorlaten
  • Return false → blokkeer de navigatie

Compleet voorbeeld

Alles bij elkaar in src/router/index.js:

// src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '@/stores/auth'

import HomeView from '../views/HomeView.vue'
import LoginView from '../views/LoginView.vue'
import RegisterView from '../views/RegisterView.vue'
import DashboardView from '../views/DashboardView.vue'
import ProfileView from '../views/ProfileView.vue'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: HomeView },
    { path: '/login', component: LoginView },
    { path: '/register', component: RegisterView },
    {
      path: '/dashboard',
      component: DashboardView,
      meta: { requiresAuth: true }
    },
    {
      path: '/profile',
      component: ProfileView,
      meta: { requiresAuth: true }
    }
  ]
})

router.beforeEach((to) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return '/login'
  }
})

export default router

Bonus: redirect terug na login

Wil je dat na inloggen de gebruiker terugkomt op de pagina die hij wilde zien? Sla het oorspronkelijke pad op als query string:

router.beforeEach((to) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return {
      path: '/login',
      query: { redirect: to.fullPath }
    }
  }
})

Lees in LoginView.vue de query en stuur na succes terug:

// LoginView.vue
const router = useRouter()
const route = useRoute()

const handleLogin = () => {
  auth.login({ ... })

  // Terug naar oorspronkelijke pagina, of dashboard als default
  const redirect = route.query.redirect || '/dashboard'
  router.push(redirect)
}

Wat heb je nu? Bezoek /profile uitgelogd → kom op /login?redirect=/profile. Na inloggen ga je direct naar /profile. Veel betere ervaring dan altijd op het dashboard belanden.

Belangrijke Regels

  • meta: { requiresAuth: true } op routes die beveiligd zijn
  • router.beforeEach doet de check — één keer, op één plek
  • Auth store binnen de guard aanroepen, niet erbuiten (Pinia moet eerst geladen zijn)
  • Login-pagina mag NIET requiresAuth hebben — anders kun je niet inloggen!
  • Echte security zit op je server — guards zijn UX, niet beveiliging

Veelgemaakte Fouten

Fout — auth store buiten guard aanroepen:

// src/router/index.js
const auth = useAuthStore()   // ❌ Pinia is hier nog niet ready

router.beforeEach((to) => {
  if (to.meta.requiresAuth && !auth.isLoggedIn) { ... }
})

Goed — binnen de guard:

router.beforeEach((to) => {
  const auth = useAuthStore()   // ✅ binnen de guard
  if (to.meta.requiresAuth && !auth.isLoggedIn) { ... }
})

Fout — login pagina als beveiligd markeren:

{
  path: '/login',
  component: LoginView,
  meta: { requiresAuth: true }   // ❌ infinite redirect!
}

De guard stuurt je naar /login/login is beveiligd → stuurt je weer naar /login → ...

Goed — login is altijd vrij toegankelijk:

{ path: '/login', component: LoginView }   // ✅

Fout — niks teruggeven bij geslaagde auth:

router.beforeEach((to) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return '/login'
  }
  return false   // ❌ alle andere navigaties geblokkeerd!
})

Goed — niks teruggeven = doorlaten:

router.beforeEach((to) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isLoggedIn) {
    return '/login'
  }
  // ✅ implicit: niks return = doorlaten
})