JAVASCRIPT
Implementing Authentication Route Guards in Vue Router 4
Secure your Vue 3 application routes by implementing global and per-route authentication guards with Vue Router 4, preventing unauthorized access.
// router/index.js (or .ts)
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
// A mock authentication service
const authService = {
isAuthenticated: () => {
// In a real app, this would check localStorage, a cookie,
// or a store for a valid token/session.
return localStorage.getItem('user_token') === 'my_super_secret_token'
},
login: (username, password) => {
if (username === 'test' && password === 'password') {
localStorage.setItem('user_token', 'my_super_secret_token')
return true
}
return false
},
logout: () => {
localStorage.removeItem('user_token')
}
}
// Define your routes
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('../views/HomeView.vue') // Example component
},
{
path: '/dashboard',
name: 'Dashboard',
component: () => import('../views/DashboardView.vue'), // Example component
meta: { requiresAuth: true } // Meta field to indicate protected route
},
{
path: '/profile',
name: 'Profile',
component: () => import('../views/ProfileView.vue'), // Example component
meta: { requiresAuth: true }
},
{
path: '/login',
name: 'Login',
component: () => import('../views/LoginView.vue') // Example component
},
{
path: '/:pathMatch(.*)*', // Catch all 404
name: 'NotFound',
component: () => import('../views/NotFoundView.vue') // Example 404 component
}
]
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes
})
// Global Navigation Guard
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const isAuthenticated = authService.isAuthenticated()
if (requiresAuth && !isAuthenticated) {
// If the route requires auth and user is not authenticated, redirect to login page
console.log('Access denied: Redirecting to login.')
next({ name: 'Login', query: { redirect: to.fullPath } })
} else if (!requiresAuth && isAuthenticated && to.name === 'Login') {
// If user is authenticated and tries to access login page, redirect to dashboard
console.log('Already logged in: Redirecting to dashboard.')
next({ name: 'Dashboard' })
} else {
// Proceed to the route
next()
}
})
export { router, authService } // Export authService for use in components
// Example of views/LoginView.vue (not part of the core router snippet, but for context)
// <template>
// <div class="login-container">
// <h2>Login</h2>
// <form @submit.prevent="handleLogin">
// <div class="form-group">
// <label for="username">Username:</label>
// <input type="text" id="username" v-model="username" required>
// </div>
// <div class="form-group">
// <label for="password">Password:</label>
// <input type="password" id="password" v-model="password" required>
// </div>
// <button type="submit">Login</button>
// <p v-if="error" class="error-message">{{ error }}</p>
// </form>
// </div>
// </template>
// <script setup lang="ts">
// import { ref } from 'vue'
// import { useRouter, useRoute } from 'vue-router'
// import { authService } from '../router' // Import authService
// const username = ref('')
// const password = ref('')
// const error = ref('')
// const router = useRouter()
// const route = useRoute()
// const handleLogin = () => {
// error.value = ''
// if (authService.login(username.value, password.value)) {
// const redirectPath = route.query.redirect as string || '/dashboard'
// router.push(redirectPath)
// } else {
// error.value = 'Invalid username or password.'
// }
// }
// </script>
// <style scoped>
// .login-container {
// max-width: 400px;
// margin: 50px auto;
// padding: 20px;
// border: 1px solid #ddd;
// border-radius: 8px;
// box-shadow: 0 2px 4px rgba(0,0,0,0.1);
// }
// .form-group {
// margin-bottom: 15px;
// }
// .form-group label {
// display: block;
// margin-bottom: 5px;
// }
// .form-group input {
// width: 100%;
// padding: 8px;
// border: 1px solid #ccc;
// border-radius: 4px;
// }
// button {
// width: 100%;
// padding: 10px;
// background-color: #007bff;
// color: white;
// border: none;
// border-radius: 4px;
// cursor: pointer;
// }
// button:hover {
// background-color: #0056b3;
// }
// .error-message {
// color: red;
// margin-top: 10px;
// text-align: center;
// }
// </style>
How it works: This snippet demonstrates how to implement authentication route guards using Vue Router 4's `beforeEach` global navigation guard. By adding a `meta: { requiresAuth: true }` field to protected routes, the guard checks if a user is authenticated via a mock `authService`. If a user tries to access a protected route without authentication, they are redirected to the login page. Conversely, an authenticated user attempting to access the login page is redirected to the dashboard. This pattern is fundamental for building secure applications by controlling access to specific parts of your UI based on user authentication status.