16

We are developing a web application in react js and using Odoo as a backend.

My web app will be hosted at abc.com and the backend is at xyz.com.

Now, when I call login API at my backend It is giving a JWT token in response. Now my question is where we should store this JWT token for subsequent use.

I found few similar questions but I am not sure where They should be stored in my use case. Can anyone please suggest to me where we should store it on the client-side?

Thanks a lot in advance!

General Grievance
  • 4,555
  • 31
  • 31
  • 45
Sam
  • 161
  • 1
  • 1
  • 3

2 Answers2

16

You have a few options where you can store the token. Each option has it's flaws, and which one should you choose depends on your concrete needs and use cases. It's also good to know that there is no secure way to store tokens in the browser.

  1. Storing in memory. You can keep the token in a variable in the script's memory. It will be hard to steal the token with an XSS attack, but you will need a new token every time the user refreshes the page.

  2. Storing in local storage. This gives you the possibility to reuse the token when the user refreshes the page. With this option, however, it's much easier for an XSS attack to steal your tokens.

  3. Storing in a httponly, secure, same-site cookie. This is currently considered as the most secure option for SPAs, but in this scenario you will not be able to set the Authorization header straight from the SPA, you can only rely on the browser to add the secure cookie to your request. This means, that you might need some additional backend components, which will extract tokens from cookies (and maybe put them in those cookies, in the first place). This is usually referred to as a Token Handler component, or a Backend-for-Frontend. If this is the level of security that you need you can have a look at an example implementation of such a BFF, that we did at Curity: https://curity.io/resources/learn/token-handler-spa-example/

Have a look also at this great video by Philippe de Ryck about security of tokens in the browser: https://pragmaticwebsecurity.com/talks/xssoauth.html

Michal Trojanowski
  • 10,641
  • 2
  • 22
  • 41
  • It would seem to make sense to extract the jwt client side and add it as bearer token to every request. – html_programmer Nov 21 '21 at 21:06
  • What you described is option 1 in my answer. It does make sense, but is less secure than keeping the token in an http-only, secure cookie. – Michal Trojanowski Nov 22 '21 at 07:36
  • 1
    I was mistaken. You cannot read an httponly cookie client side obviously. Begs the question how this improves security when XSS attack can send ajax requests to your backend and will send the cookie along with it which the backend will validate. – html_programmer Nov 22 '21 at 09:39
  • The attack can send requests only for as long as the user has the browser opened. In case of tokens, the XSS can read the token, send it to the attacker's website and then use it for as long as it's valid. But generally trying to avoid XSS should be a priority, because with either solution a successful attack will still be able to do some harm. – Michal Trojanowski Nov 22 '21 at 09:53
  • @MichalTrojanowski The curity.io link is giving 404. – Totoro Mar 15 '22 at 11:10
  • 1
    @Totoro thanks for pointing it out. It's fixed now. – Michal Trojanowski Mar 16 '22 at 08:22
  • Using cookies can expose the app to CSRF attacks. – marciopd Oct 17 '22 at 08:36
  • Not if you implement proper measures against CSRF (as recommended by OWASP). CSRF is also much easier to protect yourself against than XSS. – Michal Trojanowski Oct 17 '22 at 08:59
-1

You can store the obtained token in a cookie. If you are using the axios library for your request, you can set the token to be handled in the interceptors of axios

export function getToken() {
  for (const i in TokenKey) {
    if (Cookies.get(TokenKey[i])) return Cookies.get(TokenKey[i])
  }

service.interceptors.request.use(
  config => {
    const token = getToken() || store.getters.token
    if (token) {
      config.headers['Authorization'] = token
    }
    return config
  },
  error => {
    // Do something with request error
    console.log(error) // for debug
    Promise.reject(error)
  }

)

Przeblysk
  • 24
  • 4
  • This defeats the purpose of security using a cookie (it would then be more straight forward to use local storage). If you want a cookie, use an HttpOnly cookie so that a potential XSS attack can't steal it. You need _nothing_ in the code to use the token because the browser will automatically pass it back to the server in subsequent requests! – CherryDT Sep 23 '21 at 06:05
  • 1
    Storing Token in webStorage(localStorage,sessionStorage) can be accessed by js in the same domain, which makes it easy to be attacked by xss, especially if many third-party js libraries are introduced in the project, if js scripts are stolen, attackers can easily access your website, storing Token in cookie, you can specify httponly to prevent js from being read, and you can specify secure to ensure Token is only transmitted under HTTPS. – Przeblysk Sep 23 '21 at 06:39
  • 1
    In the cookie, you can specify httponly to prevent js from being read, you can also specify secure to ensure that the Token is only transmitted under HTTPS, the downside is that it is vulnerable to CSRF attacks. – Przeblysk Sep 23 '21 at 06:39
  • 1
    @Przeblysk If you specify httponly in a cookie, then it won't be possible to use the code you posted in the answer, as no scripts will be able to access the cookie. But it's true that it's better to store the token in an httponly cookie as it's a bit more secure. – Michal Trojanowski Sep 23 '21 at 07:34
  • @MichalTrojanowski Yes, you're right. – Przeblysk Sep 23 '21 at 08:41
  • @Przeblysk This is exactly what I said... If you don't care about XSS, it would be easier to use localStorage in the first place (because it's more straight-forward to write to and read from), but if you do, you would need to use httponly and it would mean the code you posted wouldn't work either. So I don't see any circumstance in which the approach you posted would make sense (assuming you can decide how the server works too). – CherryDT Sep 23 '21 at 11:03