Is combining Nuxt 3 with WP REST API, Pinia, and local storage an effective approach for user authentication?

My current project involves utilizing NUXT 3 for the frontend and integrating Wordpress as the backend. The data is transmitted from the backend to the frontend through the REST API. The application functions as a content management system (CMS), with all routes protected by a middleware that verifies the user's login status. This middleware checks for the existence of local storage, created upon successful user login.

The process works as follows:

  1. For every route, the middleware validates the presence of local storage userAuthentication. If it's missing, the user is redirected to the /login page. This middleware operates globally but only on the client side.

  2. Upon attempting to access the application, a POST request is sent to the Wordpress backend with the user's provided credentials. If the endpoint responds with CODE 200, the data is saved in the PINIA store, and a local storage is generated containing the response data (including the token). Subsequently, the user gains access to protected routes.

  3. When the user logs out, the local storage is deleted, and they are redirected to the /login page.

Here are some queries I have:

  1. Is this approach secure?
  2. Is the server adequately secured by Wordpress, or should I implement server middleware?
  3. Could an unauthorized individual gain access to my app by creating local storage with the same name?

I welcome any insights or suggestions.

middleware/auth.global.ts

export default defineNuxtRouteMiddleware((to, from) => {
    // isAuthenticated() serves as an example validation method for user authentication
    if (typeof window !== 'undefined') {

        const useStateLocalStorage = JSON.parse(localStorage.getItem('userAuthentication'));

        if (!useStateLocalStorage) {
            if (from.path === '/login') {
                return abortNavigation()
            }

            if (to.path !== '/login') {
                return navigateTo('/login')
            }
        }

        if (useStateLocalStorage) {
            if (to.path === '/login') {
                return abortNavigation()
            }
        }

    }
})

/login.vue

import { useUserStore } from "~/store/userAuth";

const config = useRuntimeConfig();
const signinForm = ref({ username: "", password: "" });
const userStore = useUserStore();

const signIn = async () => {

  const response = await $fetch(config.public.apiBaseUrl + '/wp-json/jwt-auth/v1/token',
      {
        method: "post",
        body: {
          'username': signinForm.value.username,
          'password': signinForm.value.password
        }
      })
      .then((response) => {

        //SUCCESS
        //console.log('LOGIN SUCCESS', response);

        //SAVE USER DATA IN PINIA STORE
        userStore.IsAuth = true;
        userStore.token = response['data']['token'];
        userStore.username = response['data']['displayName'];
        userStore.email = response['data']['email'];
        userStore.firstName = response['data']['firstName'];
        userStore.lastName = response['data']['lastName'];

        //DEBUG

        console.log(userStore.IsAuth)
        console.log(userStore.token)
        console.log(userStore.username)
        console.log(userStore.email)
        console.log(userStore.firstName)
        console.log(userStore.lastName)

        //NAVIGATE TO HOME
        navigateTo('/')

      })
      .catch((error) => {
        console.log('LOGIN ERROR', error)
      });

  //console.log(response)
  signinForm.value = {username: "", password: ""};

}

userStore.$subscribe((mutation, state) => {
  localStorage.setItem('userAuthentication', JSON.stringify(state))
})

store/userAuth.js

import { defineStore } from 'pinia'

export const useUserStore = defineStore('user', {
    state: () => {
        return {
            token: null,
            username: null,
            email: null,
            firstName: null,
            lastName: null,
            IsAuth: false
        }
    },
    persist: true,
    actions: {
        userUpdate(payload) {
            //localStorage.setItem('user-auth', payload)
            this.user = payload;
            this.IsAuth = payload;
        },
        tokenUpdate(payload) {
            //localStorage.setItem('user-auth', payload)
            this.token = payload;
        },
    }
})

Answer №1

Although I am not well-versed in the wordpress backend, my recommendation would be to refrain from storing sensitive user information in localstorage due to its vulnerability to third-party scripts.

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

Updating the scope variable in an AngularJS directive

Recently delving into Angular, I encountered an issue: I have both a list view and a details view with tags. To facilitate navigating between the two views and loading new elements from a service upon click events, I created a directive. My aim is to also ...

Puppeteer: Navigate to a specific page number

I'm encountering an issue with a table and page numbers as links, such as: 1, 2, 3, 4 etc… -> each number is a link. Attempted to create a loop to click on different page numbers. On the first iteration, I can reach page 2. However, on the secon ...

Looking for a way to make the spinner disappear in puppeteer while waiting?

What is the most effective way to wait for the spinner to vanish before clicking on a button? Check out this example ...

Creating a button in HTML that performs a POST request without redirecting involves using specific code techniques

Looking for a way to create an HTML button that triggers a POST request to a quick route with parameters passed through req.params. The challenge is preventing the button from redirecting me to the route when clicked, but instead staying on the same page w ...

Is it possible to select options in AngularJS based on certain criteria?

I created an AngularJS select menu that is currently functioning correctly: <select name="s1" id="s1" multiple="multiple" data-native-menu="false" data-role="none" ng-model="userlistSelect" ng-options="u.userId as u.fi ...

Encountering 404 errors when reloading routes on an Angular Azure static web app

After deploying my Angular app on Azure static web app, I encountered an error. Whenever I try to redirect to certain routes, it returns a 404 error. However, if I navigate from one route to another within the app, everything works fine. I have attempted t ...

load a file with a client-side variable

Is there a way to load a file inside a container while utilizing an argument to fetch data from the database initially? $('#story').load('test.php'); test.php $st = $db->query("select * from users where id = " . $id); ... proce ...

How to stop Accordion from automatically collapsing when clicking on AccordionDetails in Material-UI

I am working on a React web project with two identical menus. To achieve this, I created a component for the menu and rendered it twice in the App component. For the menu design, I opted to use the Material UI Accordion. However, I encountered an issue wh ...

Error: The document has not been defined - experimenting with vitest

I'm currently working on a Vite project using the React framework. I have written some test cases for my app using Vitest, but when I run the tests, I encounter the following error: FAIL tests/Reservations.test.jsx > Reservations Component > d ...

Manipulating div position interactively with vanilla javascript during scroll

I'm trying to create an effect where an image inside a div moves vertically downwards as the user scrolls, stopping only at the end of the page. I've attempted a solution using a script from another post, but it doesn't seem to be working. ...

Calculating variables in JavaScript

Explanation from Mozilla Documentation: console.log((function(...args) {}).length); // The result is 0 because the rest parameter is not counted console.log((function(a, b = 1, c) {}).length); // The result is 1 because only parameters before th ...

ExpressJS encountered an error due to an unsupported MIME type ('text/html') being used as a stylesheet MIME type

Whenever I start my NodeJS server and enter localhost:8080, I encounter the mentioned error while the page is loading. The head section of my index.html file is provided below. It's puzzling to me why this error is happening, considering that my index ...

The display and concealment of a div will shift positions based on the sequence in which its associated buttons are clicked

I am in need of assistance with coding (I am still learning, so please excuse any syntax errors). What I am trying to achieve is having two buttons (button A and button B) that can toggle the visibility of their respective divs (div A and div B), which sh ...

How to temporarily change CSS color for 5 seconds using JavaScript and incorporate an easing effect?

Regarding this query: JavaScript - Modifying CSS color for 5 seconds Here is a live example of the solution: http://jsfiddle.net/maniator/dG2ks/ I am interested in adding an easing effect to the transition, gradually making the color fully opaque and t ...

Utilizing FCKEditor to incorporate dimensions of width and height into image elements

I'm currently facing an issue while attempting to specify width and height for images within an old WYSIWYG FCKEditor. The challenge arises when trying to retrieve the naturalWidth/naturalHeight properties, as they return values of 0. What could I be ...

`javascript pop up notification will only display one time`

I am currently developing a chrome extension with two different modes. When the user clicks on the icon, a pop-up message should appear to indicate which mode is active. However, I am facing an issue where the message does not always display when the pop- ...

Unbounded AngularJS 1.x looping of Ag-grid's server-side row model for retrieving infinite rows

I am obtaining a set of rows from the server, along with the lastRowIndex (which is currently at -1, indicating that there are more records available than what is being displayed). The column definition has been created and I can see the column headers in ...

Sophisticated sinatra parameter

Creating Dynamic Surveys <script type="text/x-jsrender" id="surveyTemplate"> <li class="question"> <input type="text" name="survey[question]" /> <button class="add_answer">Add Answer</button> <ul> ...

Exploring ways to connect my React application with a node backend on the local network?

On my MacBook, I developed a react app that I access at http://localhost:3000. In addition, I have a nodejs express mysql server running on http://localhost:5000. The issue arises when I try to open the IP address with port 3000 in the browser of my Window ...

Tips for gracefully organizing a sequence of requests/callbacks with the dojo framework?

My experience with dojo has been pretty good, but I still have some questions about dojo.Deferred as there are features that I haven't fully explored yet. While researching, I began to wonder if using a Deferred in the following scenario would be a be ...