Search code examples
vue.jsjwtauthorizationvuex

Where to store token in Vue.js?


I read in some security blog that storing a token in localstorage is unsafe so what i want to do is to store the token in the vuex storage, and all the api call will include that token in all following request.

But I am not able to access the token in the first place during successful login, what I want to do is to store the token in the vuex storage for the first time, I thought of sending the token to the body of the response but it will be a vulnerable method so I am sending it in the header["authorization"].

below are my user.js and login.vue file respectively.

router.post('/login', function (req, res, next) {
    const {
        UserName,
        Password
    } = req.body;

    if (UserName.length == 0 || Password.length == 0) {
        res.status(400).json({
            message: 'Email or Password is empty',
        });
    } else {
        login_pool.query(
            'SELECT * FROM authentication WHERE user_name = ($1) and password = crypt(($2), password)',
            [UserName, Password],
            (err, results) => {
                if (err) {
                    throw err;
                } else if (results.rows.length == 1) {
                    // On Successful Login
                    const token = jwt.sign(
                        {
                            user_name: results.rows[0].user_name,
                            full_name: results.rows[0].full_name,
                            phone_number: results.rows[0].phone_number,
                        },
                        btoa(process.env.TOKEN_SECRET), // converting token_secret to base 64
                        { expiresIn: '1800s' },
                        { algorithm: 'HS256' },
                        (err) => {
                            if (err) {
                                res.status(400).json({
                                    message: 'Not able to create a token',
                                });
                                console.log(err);
                            }
                        }
                    );
                    res.header('Authorization', `Bearer ${token}`);
                    res.status(201).json({
                        message: results.rows[0].full_name + 'logged in.',
                    });
                    console.log(results.rows[0].full_name + 'Just Logged In. ');
                } else {
                    login_pool.query(
                        'SELECT * FROM authentication WHERE user_name = ($1)',
                        [UserName],
                        (errUser, resultUser) => {
                            if (resultUser.rows.length != 1) {
                                res.status(400).json({
                                    message: 'User with this email does not exist',
                                });
                            } else {
                                res.status(400).json({
                                    message: 'Password is not correct',
                                });
                            }
                        }
                    );
                }
            }
        );
    }
});
LoginSubmit() {
    this.axios
        .post(
            "http://127.0.0.1:3000/users/login",
            {
                UserName: this.UserName,
                Password: this.Password,
            },
            {
                headers: {
                    "Content-Type": "application/json;charset=UTF-8",
                    "Access-Control-Allow-Origin": "*",
                    Accept: "application/vnd.api+json",
                },
            }
        )
        .then(
            (res) => {
                // successful login
                console.log(res.headers); // authentication header not present here
                this.Error = "";
                console.log(this.axios.defaults.headers.common); // authentication header not present here
            },
            (err) => {
                console.log(err.response.data.message);
                this.Error = err.response.data.message.replace(/"/g, "");
            }
        );
},

Solution

  • I've never seen it done like this. A JWT can just be sent as part of the body of the response to some POST /auth endpoint. You then save that token using some sort of storage.

    Whether that is localStorage or a cookie is debatable; I personally just use localStorage so that the API can be as stateless as possible.

    Using a cookie however is more secure if you use the httpOnly setting, making the cookie inaccessible by JavaScript. The cookie is then immediately returned from your login route (so there is no processing of any return-data in your Axios script since the

    Set-Cookie: my_session=abcd1234; httponly; ...
    

    header is managed by the browser automatically)

    Vuex store is in essence a global state object that loses all its contents once you refresh your browser window. That is unless you couple it with some sort of localStorage/sessionStorage that loads/saves to your Vuex store.

    So I suggest you remove the Access-Control-Expose-Headers: Authorization header and just send the JWT token as an httpOnly cookie if this works with your authentication flow (so there is no Authorization: Bearer ... header but just a Cookie: ... header automatically sent by your browser). You obviously always want to use https because everything that is not can easily be read plain-text by some malicious owner of a (wifi-)network.