use httpOnly/sameSite cookie to make JWT authentication safer (local experiment)
Tags: httponly, graphql, token
Published: 2020-09-12 11:10:19

# 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](https://owasp.org/www-community/attacks/xss/) an [CSRF](https://owasp.org/www-community/attacks/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] (https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) from different domains. To add more difficulty to XSS and CSRF is [httpOnly](https://owasp.org/www-community/HttpOnly) and [sameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) setting in the cookie. The cookie secure parameter makes data only transfer over the HTTPS protocol, which makes [MitM](https://developer.mozilla.org/en-US/docs/Glossary/MitM) attack not easily execute. 

# prerequisite
* A React login page with GraphQL client 
* [ngrok](]https://ngrok.com/) in React root folder
* [GraphQL server ](https://dev.to/pcreem/build-a-graphql-todolist-server-on-prisma-postgresql-2j63)

# 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 }

```
_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`
```
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 
```
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