Hone logo
Hone
Problems

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:

  1. Define a beforeEach navigation guard: This function will be registered with the Vue Router.
  2. Route Metadata: Routes will have an optional meta object, which can contain a requiresAuth boolean property. If requiresAuth is true, the route is considered protected and requires authentication.
  3. Authentication Check: The guard should check if the user is authenticated. For this challenge, simulate authentication by checking a global boolean variable isAuthenticated.
  4. Conditional Redirection:
    • If a route requires authentication (requiresAuth: true) and the user is not authenticated (isAuthenticated is false), 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.
  5. 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 requiresAuth or requiresAuth: 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 /login route 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: true and no meta object 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 /login as the current route.
  • Explanation: The beforeEach guard detects that /dashboard requires authentication (meta.requiresAuth is true) and isAuthenticated is false. It calls next('/login') to redirect the user.

Scenario 2: Navigating to a Protected Route With Authentication

  • Input:
    • isAuthenticated = true (after calling simulateLogin())
    • User attempts to navigate to /dashboard
  • Expected Output:
    • Successful navigation to /dashboard.
    • The router's history will show /dashboard as the current route.
  • Explanation: The beforeEach guard checks /dashboard, finds meta.requiresAuth is true, but also sees that isAuthenticated is true. It calls next() 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 /public as the current route.
  • Explanation: The beforeEach guard checks /public, finds meta.requiresAuth is false (or undefined). It calls next() 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 /login as the current route.
  • Explanation: The beforeEach guard checks /login. Even though it's public, the logic should explicitly allow navigation to /login to avoid infinite redirect loops if a user is already logged in and tries to access the login page. In this simplified example, the meta.requiresAuth: false handles this, but a more robust solution might have an explicit check for to.path === '/login'.

Constraints

  • The solution must use Vue Router v4 (or later) and be implemented entirely in TypeScript.
  • The isAuthenticated variable 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 meta property itself is undefined on a route.
  • The next function can be called with true (or no argument) to proceed with the current navigation, with a RouteLocationRaw object to redirect, or with false to cancel the navigation. For this challenge, you'll primarily use next() and next('/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.beforeEach callback.
Loading editor...
typescript