Implementing a Navigation Guard in Vue.js with TypeScript
This challenge focuses on implementing a beforeEach navigation guard in a Vue.js application using TypeScript. Navigation guards are crucial for controlling access to routes, performing asynchronous operations before navigation, or modifying the route object. Mastering this is essential for building secure and dynamic web applications.
Problem Description
You are tasked with creating a beforeEach navigation guard for a Vue.js application. This guard should intercept all route navigations and perform a specific action based on the route's metadata. Specifically, you need to implement a check for authenticated users before allowing navigation to certain "protected" routes.
Key Requirements:
- Define a
beforeEachnavigation guard: This function will be registered with the Vue Router. - Route Metadata: Routes will have an optional
metaobject, which can contain arequiresAuthboolean property. IfrequiresAuthistrue, the route is considered protected and requires authentication. - Authentication Check: The guard should check if the user is authenticated. For this challenge, simulate authentication by checking a global boolean variable
isAuthenticated. - Conditional Redirection:
- If a route requires authentication (
requiresAuth: true) and the user is not authenticated (isAuthenticatedisfalse), redirect the user to a designated login route (e.g.,/login). - If a route does not require authentication or if the user is authenticated, allow the navigation to proceed.
- If a route requires authentication (
- TypeScript Implementation: All code, including the router configuration and the navigation guard, should be written in TypeScript.
Expected Behavior:
- Navigating to a public route (no
requiresAuthorrequiresAuth: false) should succeed regardless of authentication status. - Navigating to a protected route (
requiresAuth: true) when the user is authenticated should succeed. - Navigating to a protected route (
requiresAuth: true) when the user is not authenticated should redirect to/login. - Navigating to the
/loginroute should always be allowed, even if the user is already authenticated (to allow them to log out or re-authenticate).
Edge Cases to Consider:
- What happens if a route has
requiresAuth: trueand nometaobject is defined? (This should be treated as not requiring authentication). - How to handle the initial route load – does the guard apply? (Yes, it applies to all navigations, including the initial load).
Examples
Let's assume the following global isAuthenticated state and a simplified router setup.
Simulated Authentication State:
let isAuthenticated: boolean = false; // Initially, user is not authenticated
// Function to simulate login (for testing purposes)
function simulateLogin() {
isAuthenticated = true;
}
// Function to simulate logout (for testing purposes)
function simulateLogout() {
isAuthenticated = false;
}
Simulated Router Configuration:
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
const routes: RouteRecordRaw[] = [
{ path: '/', component: { template: '<div>Home Page</div>' } },
{
path: '/dashboard',
component: { template: '<div>Dashboard Page</div>' },
meta: { requiresAuth: true }
},
{
path: '/settings',
component: { template: '<div>Settings Page</div>' },
meta: { requiresAuth: true }
},
{
path: '/login',
component: { template: '<div>Login Page</div>' },
meta: { requiresAuth: false } // Login page is public
},
{
path: '/public',
component: { template: '<div>Public Page</div>' },
meta: { requiresAuth: false }
}
];
const router = createRouter({
history: createWebHistory(),
routes,
});
// Placeholder for the beforeEach guard to be implemented
router.beforeEach(async (to, from, next) => {
// Your implementation goes here
});
export default router;
Scenario 1: Navigating to a Protected Route Without Authentication
- Input:
isAuthenticated = false- User attempts to navigate to
/dashboard
- Expected Output:
- Redirection to
/login - The router's history will show
/loginas the current route.
- Redirection to
- Explanation: The
beforeEachguard detects that/dashboardrequires authentication (meta.requiresAuthistrue) andisAuthenticatedisfalse. It callsnext('/login')to redirect the user.
Scenario 2: Navigating to a Protected Route With Authentication
- Input:
isAuthenticated = true(after callingsimulateLogin())- User attempts to navigate to
/dashboard
- Expected Output:
- Successful navigation to
/dashboard. - The router's history will show
/dashboardas the current route.
- Successful navigation to
- Explanation: The
beforeEachguard checks/dashboard, findsmeta.requiresAuthistrue, but also sees thatisAuthenticatedistrue. It callsnext()to allow the navigation.
Scenario 3: Navigating to a Public Route
- Input:
isAuthenticated = false- User attempts to navigate to
/public
- Expected Output:
- Successful navigation to
/public. - The router's history will show
/publicas the current route.
- Successful navigation to
- Explanation: The
beforeEachguard checks/public, findsmeta.requiresAuthisfalse(or undefined). It callsnext()to allow the navigation.
Scenario 4: Navigating to the Login Route While Authenticated
- Input:
isAuthenticated = true- User attempts to navigate to
/login
- Expected Output:
- Successful navigation to
/login. - The router's history will show
/loginas the current route.
- Successful navigation to
- Explanation: The
beforeEachguard checks/login. Even though it's public, the logic should explicitly allow navigation to/loginto avoid infinite redirect loops if a user is already logged in and tries to access the login page. In this simplified example, themeta.requiresAuth: falsehandles this, but a more robust solution might have an explicit check forto.path === '/login'.
Constraints
- The solution must use Vue Router v4 (or later) and be implemented entirely in TypeScript.
- The
isAuthenticatedvariable should be treated as a global state (you don't need to implement a complex authentication system, just use the provided boolean). - The navigation guard should be asynchronous if there were any asynchronous checks (though for this challenge, synchronous checks are sufficient for
isAuthenticated). - The
next()function of the navigation guard must be called exactly once per navigation.
Notes
- Remember to import necessary types from
vue-router(e.g.,NavigationGuardNext,RouteLocationNormalizedLoaded). - Consider how you would handle the case where the
metaproperty itself isundefinedon a route. - The
nextfunction can be called withtrue(or no argument) to proceed with the current navigation, with aRouteLocationRawobject to redirect, or withfalseto cancel the navigation. For this challenge, you'll primarily usenext()andnext('/path'). - You are implementing the guard within a Vue application context. The examples show how it would be integrated with a Vue Router instance. Your task is to write the logic for the
router.beforeEachcallback.