DEV Community

pcreem
pcreem

Posted on • Edited on

use httpOnly/sameSite cookie to make JWT authentication safer (local experiment)

the pros and cons of storing token in local storage

One of the common ways to store a token on the front end is local-storage with the benefit of larger store sizes than a cookie and operate without a server. But the method also bring XSS an CSRF attack concern, such as
<img src="http://domain.not.exist" onerror=alert(localStorage.getItem.token);>

another token storage possibility

XSS execute by script, CSRF works when the original cookie sent to origin URL from different domains. To add more difficulty to XSS and CSRF is httpOnly and sameSite setting in the cookie. The cookie secure parameter makes data only transfer over the HTTPS protocol, which makes MitM attack not easily execute.

prerequisite

conceptual code and setting

A. server side

code ./resolvers/mutation.js



async function login(parent, args, context, info) {
  //...user email/password verification
  const token = jwt.sign({ userId: user.id }, APP_SECRET)
  const options = {
    maxAge: 1000 * 60 * 15 , //expires in 15 minutes
    httpOnly: true, // client can't get cookie by script
    secure: true, // only transfer over https
    sameSite: true, // only sent for requests to the same FQDN as the domain in the cookie
  }
  context.request.res.cookie('token', token, options)

//Can't get token at the first client login request
  const returnToken = () => context.request.headers.cookie

  let { userId } = jwt.verify(returnToken().replace('token=', ''), APP_SECRET)
  if (userId === user.id) {
    console.log('ok') //checking result on server console
    return {
      user, //send user information to client
    }
  }
  throw new Error('Invalid request')

}

module.exports = { login }



Enter fullscreen mode Exit fullscreen mode

please note the code can't get a cookie token at the first client request

B. Client-side

  • add to the end of code package.json ```

...
},
"proxy": "http://localhost:4000"
}

_After adding proxy and run on local, there will be a hosting error, which needs a configuration in .env. Remember to change your server API URL setting accordingly._

* `code .env`
Enter fullscreen mode Exit fullscreen mode

DANGEROUSLY_DISABLE_HOST_CHECK=true

**The setting is dangerous as it named, please don't use it in production**  

### C. Start project 
* on server
`node index.js`

* on client
`npm start`
`.\ngrok.exe http 3000`

### D. Check result 
1. copy the ngrok **https** URL to the browser, then run your login on the client, if setting success, you will see 
  * the result on the Application field. The httpOnly, secure field should be marked and sameSite field named with strict
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w5w5xwjhnhnuu25ratjq.png)
  * an error on your server
`TypeError: Cannot read property 'replace' of undefined`
The reason is the login function can't send and get a cookie in the first client request.

2. send a login request again, there will be an `ok` on the server console, which means the server get token in the cookie. 

3. The client will get user information after token verify. In the case is a todo page. 
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/jiqcwk298gzbs6actmkl.png)

### E. test httpOnly/https effect
* httpOnly: you can write a javascript to get the cookie 
Enter fullscreen mode Exit fullscreen mode

import Cookies from 'js-cookie'
export const getToken = () => Cookies.get(AUTH_TOKEN);

* https: use localhost:3000 or ngrok Http URL to test

# Issues and [Reference](https://medium.com/@ryanchenkie_40935/react-authentication-how-to-store-jwt-in-a-cookie-346519310e81)
1. Solve cookie token res/req in a more efficient way
2. Set refresh cookie for a smooth user experience
3. how/where to store and transfer user data 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)