Skip to content

Vue Router and RBAC

Routing is more than mapping a URL to a component. In a back-office application it connects authentication, menus, permissions, data loading, code splitting, and page restoration. The security boundary remains on the server; the frontend controls navigation and visibility as a user-experience layer.

Permissions as route metadata

ts
declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth: boolean
    permissions?: string[]
    title: string
  }
}

const routes = [
  {
    path: '/activities/publish',
    component: () => import('@/views/activity/PublishView.vue'),
    meta: {
      requiresAuth: true,
      permissions: ['activity:publish'],
      title: 'Publish campaign',
    },
  },
]

Typed metadata turns a missing permission declaration into a compile-time issue. Menus, pages, and controls should consume the same permission vocabulary rather than maintain three unrelated rule sets.

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

  if (!to.meta.requiresAuth) return true
  if (!auth.initialized) await auth.restoreSession()
  if (!auth.loggedIn) return { name: 'login', query: { redirect: to.fullPath } }
  if (!auth.hasAny(to.meta.permissions)) return { name: 'forbidden' }
})

Session restoration belongs in a store and permission evaluation belongs in a domain function. The guard only coordinates their order. This keeps navigation logic deterministic and testable.

Dynamic route lifecycle

When routes are driven by a server-side menu, use stable route names or IDs for deduplication and preserve the removal function returned by addRoute. Remove old routes before loading permissions for a new user or role.

ts
const disposers = serverRoutes.map(route => router.addRoute(toRouteRecord(route)))

function resetDynamicRoutes() {
  disposers.splice(0).forEach(dispose => dispose())
}

Four permission layers

LayerFrontend responsibilityFinal enforcement
MenuWhether an entry is visibleAuthorized menu or permission response
RouteWhether navigation is allowedAPIs must still authorize
PageWhether an area and its data are shownServer filters data for the user
OperationWhether a control is visible or enabledWrite API authorizes and audits

Role switching must clear user state, dynamic routes, cached pages, and in-flight requests before loading the new permission set. Otherwise the new session may inherit visible UI or data from the previous role.

References: Navigation Guards, Route Meta Fields, and Navigation Failures.