Nuxt middleware failing to verify user's logged-in status

I am currently working on implementing user authentication and redirection logic based on the user's authentication status. For instance, if a logged-in user tries to access the login or signup page, they should be automatically redirected. To achieve this, I have set up middleware functions.

After logging in the user, the authenticateUser action is triggered, creating the user and setting the necessary cookies and browser local storage data. However, despite verifying that these are correctly set, I encounter an issue where the redirection does not occur when visiting the login page post-login.

The first middleware, located in altauth.js, checks for user authentication as follows:

export default function (context) {
    console.log(context.store.getters('profile/isAuthenticated'))
    if (context.store.getters.isAuthenticated) {
        context.redirect('/')
    }
}

Additionally, token persistence is managed through another middleware, checkauth.js, which utilizes both Cookies and local storage to store the token:

export default function (context) {
    if(context.hasOwnProperty('ssrContext')) {
        context.store.dispatch('profile/initAuth', context.ssrContext.req);
    } else {
        context.store.dispatch('profile/initAuth', null);
    }
}

Furthermore, below are the key store values pertaining to the implemented functionality:

import Cookie from 'js-cookie';

// State
export const state = () => ({
    token: null,
})

// Mutations
export const mutations = {
    setToken(state, token) {
        state.token = token
    },
    clearToken(state) {
        state.token = null
    }
}

// Actions
export const actions = {
    async authenticateUser(vuexContext, authData) {
        // Authentication API endpoint URL selection based on operation type
        let authUrl = 'https://look.herokuapp.com/signup/'
        if (authData.isLogin) {
            authUrl = 'https://look.herokuapp.com/login/'
        }

        return this.$axios
        .$post(authUrl, authData.form)
        .then(data => {
            console.log(data);
            
            // Saving received token
            const token = data.token
            vuexContext.commit('setToken', token)
            localStorage.setItem("token", token)
            Cookie.set('jwt', token);
        })
        .catch(e => console.log(e))
    },
    initAuth(vuexContext, req) {
        // Initializing user authentication with stored token
        let token
        if (req) {
            if (!req.headers.cookie) {
                return;
            }
            // Retrieving JWT token from cookie header
            const jwtCookie = req.headers.cookie
                .split(';')
                .find(c => c.trim().startsWith('jwt='));
            if (!jwtCookie) {
                return;
            }
            token = jwtCookie.split('=')[1];
        } else {
            // Fallback to local storage if no server request present
            token = localStorage.getItem('token');
            if (!token) {
                return;
            }
        }
        // Setting token in Vuex store
        vuexContext.commit('setToken', token);
    }
}

// Getters
export const getters = {
    isAuthenticated(state) {
        return state.token != null;
    },
}

If anyone could provide insights into what might be causing the issue, I would greatly appreciate it.

Answer №1

If you're looking for an example of an authentication system in a SSR Nuxt project, here is a basic but comprehensive one:

To implement this, you will need two APIs - one that returns token information along with user details, and another that only returns user information.

For instance:

POST http://example.com/api/auth/authorizations
{
    token: 'abcdefghijklmn',
    expired_at: 12345678,
    user: {
        name: 'Tom',
        is_admin: true
    }
}

// This requires authentication
GET http://example.com/api/auth/user
{
    name: 'Tom',
    is_admin: true
}

nuxt.config.js

plugins:[
    '~plugins/axios',
],
buildModules: [
    '@nuxtjs/axios',
],
router: {
    middleware: [
        'check-auth'
    ]
},

./pages/login.vue

<template>
    <form @submit.prevent="login">
        <input type="text" name="username" v-model="form.username">
        <input type="password" name="password" v-model="form.password">
    </form>
</template>
<script type="text/javascript">
export default{
    data(){
        return {
            form: {username: '', password: ''}
        }
    },
    methods: {
        login(){
            this.$axios.post(`/auth/authorizations`, this.form)
                .then(({ data }) => {
                    let { user, token } = data;
                    this.$store.commit('auth/setToken', token);
                    this.$store.commit('auth/updateUser', user);
                    this.$router.push('/');
                })
        }
    }
}
</script>

store/index.js

const cookieFromRequest = (request, key) => {
    if (!request.headers.cookie) {
        return;
    }
    const cookie = request.headers.cookie.split(';').find(
        c => c.trim().startsWith(`${key}=`)
    );
    if (cookie) {
        return cookie.split('=')[1];
    }
}
export const actions = {
    nuxtServerInit({ commit, dispatch, route }, { req }){
        const token = cookieFromRequest(req, 'token');
        if (!!token) {
            commit('auth/setToken', token);
        }
    }
};

middleware/check-auth.js

export default async ({ $axios, store }) => {
    const token = store.getters['auth/token'];
    if (process.server) {
        if (token) {
            $axios.defaults.headers.common.Authorization = `Bearer ${token}`;
        } else {
            delete $axios.defaults.headers.common.Authorization;
        }
    }
    if (!store.getters['auth/check'] && token) {
        await store.dispatch('auth/fetchUser');
    }
}

store/auth.js

import Cookie from 'js-cookie';

export const state = () => ({
    user: null,
    token: null
});

export const getters = {
    user: state => state.user,
    token: state => state.token,
    check: state => state.user !== null
};

export const mutations = {
    setToken(state, token){
        state.token = token;
    },
    fetchUserSuccess(state, user){
        state.user = user;
    },
    fetchUserFailure(state){
        state.user = null;
    },
    logout(state){
        state.token = null;
        state.user = null;
    },
    updateUser(state, { user }){
        state.user = user;
    }
}

export const actions = {
    saveToken({ commit }, { token, remember }){
        commit('setToken', token);
        Cookie.set('token', token);
    },
    async fetchUser({ commit }){
        try{
            const { data } = await this.$axios.get('/auth/user');
            commit('fetchUserSuccess', data);
        }catch(e){
            Cookie.remove('token');
            commit('fetchUserFailure');
        }
    },
    updateUser({ commit }, payload){
        commit('updateUser', payload);
    },
    async logout({ commit }){
        try{
            await this.$axios.delete('/auth/authorizations');
        }catch(e){}
        Cookie.remove('token');
        commit('logout');
    }
}

plugins/axios.js

export default ({ $axios, store  }) => {

    $axios.setBaseURL('http://example.com/api');

    const token = store.getters['auth/token'];
    if (token) {
        $axios.setToken(token, 'Bearer')
    }

    $axios.onResponseError(error => {
        const { status } = error.response || {};
        if (status === 401 && store.getters['auth/check']) {
            store.commit('auth/logout');
        }
        else{
            return Promise.reject(error);
        }
    });
}

You have the flexibility to customize your middleware as needed, like checking authentication status:

middleware/auth.js

export default function ({ store, redirect }){
    if (!store.getters['auth/check']) {
        return redirect(`/login`);
    }
}

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

Issue: [next-auth]: `useSession` function should be enclosed within a <SessionProvider /> tag in order to work properly

Encountering an error loading my ProfilePage where the page loads fine in the browser but an error appears in the terminal. Error: [next-auth]: `useSession` must be wrapped in a <SessionProvider /> All child components are properly wrapped using Se ...

Develop a custom chat feature in HTML with the powerful VueJs framework

Currently, I've been developing a chat plugin for HTML using VueJs. My dilemma lies in creating a versatile plugin that can seamlessly integrate into any website. My ultimate goal is to design a plugin that can be easily deployed on various websites ...

Extract and loop through JSON data containing various headers

Having no issues iterating through a simple JSON loop, however, the API I am currently utilizing returns a response with multiple headers. Despite my efforts in various methods to access the results objects, I still face some challenges. The response struc ...

Maintain authentication state in React using express-session

Struggling to maintain API login session in my React e-commerce app. Initially logged in successfully, but facing a challenge upon page refresh as the state resets and I appear as not logged in on the client-side. However, attempting to log in again trigge ...

Ways to access a field value in SAPUI5

As I delve into OPENUI5/SAPUI5, I'm faced with the task of accessing data for the controls I've implemented. For instance: <Label text="Amount" /> <Input id="inputAmount" value="{Amount}" /> <Text id="lblCurrency" text="USD" > ...

The Vuetify Datepicker has a multiple property enabled, yet it is displaying an incorrect count of selected dates

I need to display a date field, but after setting multiple selection to true, the picker is showing the wrong value. Here is the code I added: <v-date-picker :landscape="$store.state[parentName].landscape" v-model="$store.state[parentNam ...

Is there a way to retrieve the list element that was added after the HTML was generated?

How can I reference a list element added using the append function in jQuery within my onclick function? See my code snippet below: $(function() { let $movies = $('#showList') let $m = $('#show') $.ajax({ method: 'GET ...

Monitor the change in FileReader().readyState using AngularJS

Below is the AngularJS code I have written to read a local file. var files = document.getElementById("file"); files.addEventListener("change", handleFile, false); function handleFile(event) { SpinnerService.upload(); // Display loading spinner ...

Load data from a JSON flat file and dynamically populate new <li> elements with it

I'm attempting to utilize data from a json flat file in order to: Create new list items Fill specific classes within the newly created list items The json data appears as follows: { "event": { "title": "Title of event", "preface": "Prefa ...

How can we display the first letter of the last name and both initials in uppercase on the JavaScript console?

I'm a new student struggling with an exercise that requires writing multiple functions. The goal is to create a function that prompts the user for their first and last name, separates the names using a space, and then outputs specific initials in diff ...

Utilize JavaScript to target the specific CSS class

Hello, I am currently using inline JS in my Wordpress navigation menu, redirecting users to a login page when clicked. However, I have been advised to use a regular menu item with a specific class and then target that class with JS instead. Despite searchi ...

Traditional method for comparing prevProps in componentDidUpdate

As I work on prototyping, I've noticed that when new props come in as an array of complex objects, prevProps.myProp===this.props.myProp always returns false. While using JSON.stringify for comparison seems to work, it doesn't feel very reliable. ...

The concept of circular dependencies in ES6 JavaScript regarding require statements

After transferring a significant amount of data onto the UI and representing them as classes, I encountered some challenges with managing references between these classes. To prevent confusing pointers, I decided to limit references to the data classes to ...

Looking up a destination with the Google Places API

My dilemma lies in dealing with an array of place names such as 'Hazrat Nizamuddin Railway Station, New Delhi, Delhi, India' and similar variations. These variations serve as alternative names for the same location, adding complexity to my task. ...

Only on mobile devices, Material-UI components spill over the screen

Update: This issue is isolated to one specific page. Other pages within the website are displaying correctly. Recently, I developed a web page using React and Material-UI. The main components utilized are Grid and Container. While the layout appears fine ...

Tips for effectively handling a prop in an HTML form using Vue.js and avoiding infinite loops with a computed property

I have a Vue.js form for signing up that features numerous similar fields. From my understanding, I should initialize the values as props rather than objects or arrays (since they will be pass-by-value). My approach involves using a computed property with ...

Unable to locate video identifier on ytdl

Lately, I have been working on developing a music bot for Discord. I have successfully managed to make it join and leave voice channels flawlessly, but playing music has proven to be quite challenging. For some reason, the play and playStream functions do ...

Ways to simulate a constant that acts as a dependency for the service being examined?

I'm currently experimenting with a file named connect-key.js. It relies on a dependency called keyvault-emulator. Content of File #1: // connect-key.js file const { save, retrieve, init } = require('./keyvault-emulator') .... .... .... // ...

Steps to extract viewmodel information from a specific controller following an ajax request

I am encountering an issue with passing updated data from my controller to the view after making an Ajax call. Here is a simplified version of what I am trying to achieve: Javascript $ANALYZE = $('#submitID'); $ANALYZE.click(function () { ...

Modifying arrays in Vue does not cause the DOM to update

I am facing an issue where I am trying to make modifications to an array in the parent component, which is passed down to the child component through Props. However, despite my efforts to insert and update data, the DOM does not reflect these changes. Par ...