Can I restrict access to all routes except one in vue-router? Is this a safe practice? Should I explore alternative methods for achieving this?

I am looking to create an online exam consisting of 5 pages, each with a countdown timer set at 120 seconds and 4 questions on each page. Once the timer runs out, users will be automatically redirected to the next page, or they can manually click the "next" button before that.

Utilizing Laravel 5.4 and VueJs, I have implemented an Ajax request for every question answered by the user. However, my main goal is to prevent users from navigating back to previous pages or viewing other pages until the designated time limit has elapsed. Is this even feasible?

My plan is to develop this application using Vuejs and vue-router, although I am unsure about how to integrate this functionality using vue-router. Despite conducting research, I haven't found much information regarding this particular issue!

Alternatively, should I consider abandoning vue-router and just implement a simple router of my own, like:

$("#page1").show();
$("#page2").hide();
$("#page3").hide();
.
.
// after 120 secs 
$("#page1").hide();
$("#page2").show();
$("#page3").hide();
.
.
 // Nevertheless, I believe this approach may not provide adequate security!

Any insights or suggestions would be highly valued. Thank you in advance.

UPDATE: This exam allows users to view a list of randomly chosen English words sourced from the words table exclusively! Users then proceed to click on any word whose meaning they know, triggering an ajax request for each selection to save the word's ID in the results table. Additionally, there exists a fake_words table wherein 50 random words are selected alongside genuine ones. If a user clicks on fake words more than 3 times, the test will result in failure. The final outcome will reveal the user's vocabulary proficiency level.

UPDATE 2: Although I attempted to implement this functionality using vue-router, I realized that all questions are randomly fetched from the database in one query, sent (via ajax) to the browser before the exam commences. This begs the question - should I segregate them into separate arrays and allocate each array to a distinct page? Do I have no choice but to do so? Can't I simply use a single v-for loop for all questions? Suppose I opt to modify the number of questions; would this necessitate manual code updates and creating new vue-router pages or removing existing ones?

Answer №1

If you are dealing with high-risk code, such as an exam, it is important to reassess your approach: "Never trust the client". Consider implementing backend solutions to address potential security issues.

1) Secure the endpoint using middleware that:

2) Generates a timestamp when the page is visited

3) Prevents users from submitting a new post (answer) after 120 seconds

Note: It's essential for users to answer online as well for better security. If the question is displayed in the browser window, users can always cache it and even take screenshots regardless of encryption measures.

edit: Utilize pagination to present questions one at a time within a specific timeframe.

edit 2: Implement a feature to send a notification to the server if devtools are opened. Check out https://github.com/sindresorhus/devtools-detect

Answer №2

Maybe these code snippets could provide some assistance:

let application = new Vue({
    element: '#application',
    data: {
        firstStep: 1,
    }
});

<step v-if="step==1"></step>

timeHandler = setInterval(function() {
    moveForwardToNextStep(2);
    this.$parent.firstStep = 0;
}.bind(this), 12000);

Answer №3

When it comes to security, the choice of a JS framework may not have a significant impact. Vue.js is a solid option for development. Here's a snippet of code to help you get started:

<template>
  <div>
    <div id="question" v-if="question">
      {{question}}
      <button @click="nextQuestion();" v-if="hasNextQuestion()"></button>
    </div>
  </div>
</template>

<script>
  export default {
    data() {
      return {
        curQuestionNo: 0,
        maxQuestions: 4
        question: null,
        timer: null
      }
    },
    methods: {      
      fetchQuestion () {
        var url = 'https://myapi.com/question?curQuestionNo=' + this.curQuestionNo;

        axios.get(url)
          .then(res => {
            this.question = res.question;
            this.curQuestionNo++;
            this.stopTimer();
            if(this.hasNextQuestion()) {
              this.startTimer();
            }
          })
          .catch(() => {
            // handle errors here
          });
      },
      startTimer () {
        this.timer = setTimeout(() => {
          this.nextQuestion();
        }, 120 * 1000);
      },
      stopTimer () {
        clearTimeout(this.timer);
      },
      nextQuestion () {
        if(this.curQuestionNo > 0) {
          this.markAnswered(function() {  
            this.fetchQuestion();
          })
        } else {
          this.fetchQuestion();
        }
      },
      markAnswered (cb) {
        var url = 'https://myapi.com/mark-complete?curQuestionNo=' + this.curQuestionNo;

        axios.post(url) 
          .then(res => {
            this.fetchQuestion();
          })
          .catch(() => {
            // error handling
          });
      },
      hasNextQuestion () {
        return this.maxQuestions - (this.curQuestionNo + 1) > 0;
      }
    },
    created () {
      this.nextQuestion();      

    }
  }
</script>

For server-side implementation, consider the following approach:

  1. Send one question at a time per request to the client.
  2. Ensure that the user has answered the previous question or the allowed time has expired before returning the next question. Keep track of the last answered question number (or expiry time) per user in the data store. The server should only send the next question after verifying this information.

Sample Code:

Route::get('question', function(){
    $curQuestionNo = intVal(Input::get('curQuestionNo'));
    $user = User::find($userId); 
    if($user->current_question_number === $curQuestionNo) {
      $question = Question::where('question_no', $curQuestionNo + 1);
      $question->sent_at = time(); 
      return $question
    } else {
      // deny access
    }
});

Route::post('mark-complete', function(){
    $curQuestionNo = intVal(Input::get('curQuestionNo'));
    $user = User::find($userId); 
    $question = Question::find($curQuestionNo);
    if($question->sent_at > 120 seconds) {
     // do not record answers
    } else {
      // record answers
    }
    $user->current_question_number = $curQuestionNo;
    $user->save();
});    

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Effective approach for incorporating external stylesheets and additional resources in Vue

When it comes to loading style sheets in Vue, what is considered the most effective method for placement? Should code or style sheets be loaded within the components that utilize them, or is it more favorable to load the resource in the parent page/contai ...

Scrolling to top using jQuery - Problem with incorrect anchor

I am attempting to implement a scrollTop animation that scrolls to an anchor within a fullscreen <section>. However, the issue is that it does not scroll to the correct anchor on the first click. Below is the code snippet. <nav id="scroller"> ...

Navigating with React Router using URL parameters

After implementing react router with a route path taskSupport/:advertiserId that includes parameters, I encountered an issue when trying to access the link http://localhost:8080/taskSupport/advertiserId. My browser kept returning 404 (Not found) errors for ...

How can the background of a div be altered when text is typed into an input field?

I need help with the following code. I want to be able to change the background color of a div with the class "target_bg" from red (default) to green every time someone enters or types text into the input field. <div class="target_bg"></div> & ...

A guide to showcasing JSON data on a webpage using JavaScript

I am currently working on a SOAP WSDL invocation application in MobileFirst. The response I receive from the SOAP WSDL is in JSON format and is stored in the result variable. When trying to access the length of the response using result.length, I encounter ...

Should WordPress files be kept separate (php, css, and js) or combined into a single file?

Currently, I am updating my WordPress website with a goal of minimizing the number of plugins used. To achieve this, I prefer writing code only for essential functionalities. In order to optimize my work with php, css, and javascript files, I have been exp ...

What is the best way to alternate $httpBackend when[method] declarations in unit tests to handle multiple requests?

When conducting my testing, I set up the model data and mock the response: beforeEach(function(){ var re = new RegExp(/^http\:\/\/.+?\/users-online\/(.+)$/); $httpBackend.whenGET(re).respond({id:12345, usersOnline:5000}); }) ...

Utilizing Vue's shorthand syntax for interpolation within attributes allows for a more concise and efficient

I'm struggling with compiling a .Vue file in my Vue.js and Laravel SPA. Currently, I am attempting to add the following code snippet to my Home.vue: <ais-index app-id="{{ config('scout.algolia.id') }}" api-key="{{ env('ALGOLIA_SEAR ...

Learn the trick to make this floating icon descend gracefully and stick around!

I'm trying to create a scrolling effect for icons on my website where they stay fixed after scrolling down a certain number of pixels. I've managed to make the header fixed after scrolling, but I'm unsure how to achieve this specific effect. ...

Viewing HTML web pages using Mozilla Firebox

Printing an HTML table with lots of content has been a challenge for me. Google Chrome didn't work, so I switched to Mozilla Firefox. However, now Firefox is breaking the page inside the table. My question is how can I trigger print preview in Firefox ...

Steps for retrieving a Unicode string from PHP using AJAX

In order to retrieve Unicode strings from PHP for my project, I figured that using AJAX would be the most suitable method. $.ajax({ url: './php_page.php', data: 'action=get_sum_of_records&code='+code, ...

Refresh the content with an embedded iframe

I am attempting to update the body content by removing all existing content and inserting an iframe: function create_custom_iframe(target){ var iframe = document.createElement('iframe'); iframe.setAttribute('id', 'target_u ...

Troubleshooting: Issues with Custom Image Loader in Next.js Export

I'm encountering a problem while attempting to build and export my Next.JS project. The issue lies with Image Optimization error during the export process. To address this, I have developed a custom loader by creating a file /services/imageLoader.js ...

Extracting and transforming an array into a list with the desired outcome

Looking for a way to flatten the array below into a single line array using Typescript/JavaScript? Student: any = [ { "id": "1", "name": "Jhon", "Marks": { "Math": "90", "English": "80", "Science": "70" } }, { "id": "2", "name": "Peter", "Marks": { "M ...

How to initiate a refresh in a React.js component?

I created a basic todo app using React, TypeScript, and Node. Below is the main component: import * as React from "react" import {forwardRef, useCallback, useEffect} from "react" import {ITodo} from "../types/type.todo" import ...

Ways to stop CKEDITOR from automatically saving textarea or contenteditable content

I've integrated the CKEDITOR plugin for a format toolbar feature on my web application. It seems that the message shown above is a default one provided by CKEDITOR. My goal is to have users start with a blank textarea every time they visit the page, ...

Issues with PHP not properly accepting JSON data sent via Ajaxor

I've been attempting to send JSON data to a PHP file using Ajax. Here is the JavaScript code I've written: function updateJSON(){ var xmlhttpa; if (window.XMLHttpRequest){ xmlhttpa = new XMLHttpRequest(); } else { xml ...

Error: The property 'getClientRects' cannot be read because it is null

I'm brand new to learning about React and I've been attempting to incorporate the example found at: Unfortunately, I've hit a roadblock and can't seem to resolve this pesky error message: TypeError: Cannot read property 'getClient ...

Meta tag information from Next.js not displaying properly on social media posts

After implementing meta tags using Next.js's built-in Head component, I encountered an issue where the meta tag details were not showing when sharing my link on Facebook. Below is the code snippet I used: I included the following meta tags in my inde ...

jQuery floating sidebar that dynamically adjusts position based on content overflow

I've come across an interesting scenario with a fiddle that I'm working on: http://jsfiddle.net/yFjtt/1/ The main concept is to allow users to scroll past the header and have the sidebar 'stick' in place as they continue scrolling down ...