2

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, "");
            }
        );
},
desertnaut
  • 57,590
  • 26
  • 140
  • 166
degod
  • 33
  • 1
  • 1
  • 7
  • 1
    *”sending the token to the body of the response but it will be a vulnerable method”* — Uhm, how so? – deceze May 16 '21 at 08:05
  • I think it can be Intercepted. – degod May 16 '21 at 09:12
  • 1
    Everything can be intercepted. I'd suggest to estimate possible risks and study the reasoning behind statements regarding the unsafety of local storage first. Otherwise this approach to security is pretty much a tinfoil hat. – Estus Flask May 16 '21 at 09:15
  • Possible duplicate of https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs – Estus Flask May 16 '21 at 09:15
  • Do you know any way to access the Authorization Header in the Font ENd – degod May 16 '21 at 09:23

2 Answers2

4

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.

Flame
  • 6,663
  • 3
  • 33
  • 53
  • > whatever you do, don’t store a JWT in local storage (or session storage). If any of the third-party scripts you include in your page is compromised, it can access all your users’ tokens. https://blog.logrocket.com/jwt-authentication-best-practices – Dagrooms May 17 '23 at 23:29
  • @Dagrooms thx I've updated the answer to opt for a `httpOnly` cookie instead. The `Authorization: Bearer ...` flow is then no longer available though. Note that if any of your front-end javascript is compromised, it actually doesn't make a difference if the attacker has the actual session token or that he can just modify anything you do on the website (e.g. he can just send an AJAX request to `POST /make-payment` and the cookie will be sent. You don't need to read it) – Flame May 18 '23 at 11:31
0
res.header({
   Authorization: "Bearer" + token,
   "Access-Control-Expose-Headers": "Authorization",
});

Using Access-Control-Expose-Headers solved my problem now I can access the Authorization Header at the frontend part by using res.headers["authorization"]

degod
  • 33
  • 1
  • 1
  • 7
  • `Authorization` is a request header, it shouldn't be used for what you're trying to achieve. If you really want to return the token in a header, use your own header (you will still have to expose it). That said, it looks like in your case you could just use sessions with cookies. – Michal Trojanowski May 17 '21 at 08:39