26

I am developing a REST application with its own authentication and authorization mechanism. I want to use JSON Web Tokens for authentication. Is the following a valid and safe implementation?

  1. A REST API will be developed to accept username and password and do the authentication. The HTTP method to be used is POST so that there is no caching. Also, there will be SSL for security at the time of transit
  2. At the time of authentication, two JWTs will be created - access token and refresh token. Refresh token will have longer validity. Both the tokens will be written in cookies, so that they are sent in every subsequent requests
  3. On every REST API call, the tokens will be retrieved from the HTTP header. If the access token is not expired, check the privileges of the user and allow access accordingly. If the access token is expired but the refresh token is valid, recreate new access token and refresh token with new expiry dates (do all necessary checks to ensure that the user rights to authenticate are not revoked) and sent back through Cookies
  4. Provide a logout REST API that will reset the cookie and hence subsequent API calls will be rejected until login is done.

My understanding of refresh token here is:

Due to the presence of refresh token, we can keep shorter validity period for access token and check frequently (at the expiry of access token) that the user is still authorized to login.

Please correct me if I am wrong.

Saptarshi Basu
  • 8,640
  • 4
  • 39
  • 58

4 Answers4

23

A REST API will be developed to accept username and password and do the authentication. The HTTP method to be used is POST so that there is no caching. Also, there will be SSL for security at the time of transit

This is the way most do it, so you're good here.

At the time of authentication, two JWTs will be created - access token and refresh token. Refresh token will have longer validity. Both the tokens will be written in cookies so that they are sent in every subsequent requests

Storing the tokens in cookies I not dangerous in itself, but if you somehow get you JWT module on your server to read them from there you vulnerable to CSRF attacks where any webpage can trigger a users browser to send a form + you sites cookie to your server unless you use CSRF tokens. So generally they are stored in localStorage and "manually" added to request headers every time.

On every REST API call, the tokens will be retrieved from the HTTP header. If the access token is not expired, check the privileges of the user and allow access accordingly. If the access token is expired but the refresh token is valid, recreate new access token and refresh token with new expiry dates (do all necessary checks to ensure that the user rights to authenticate are not revoked) and sent back through Cookies

Apart from the cookie dangers, it seems safe.

Provide a logout REST API that will reset the cookie and hence subsequent API calls will be rejected until login is done.

You don't even need to make an API call, you can simply just purge the cookies or the localStorage object and make sure your client doesn't break on missing tokens.

The standard for the express-jwt module expects the tokens to be in its own "Authorization: Bearer [Token]" header, which I would strongly recommend over cookies. The localStorage API is available all the way back to IE8 so you should be good.

Edit:

First, it's important to know the difference between XSS and CSRF attacks since they're often believed to be the same thing.

XSS is when users get unsafe JS running on your domain in other users browsers when that happens neither JWT in localStorage or sessions and JWT in cookies are safe. With httpOnly flag on cookies, you can't directly access them, but the browser will still send them with AJAX requests to your server. If this happens you generally out of luck. To prevent this, make sure to escape all user input if it's sent to the browser.

If you load 3rd party JS with script tags or iframes this might compromise localStorage unless you are careful, but I haven't worked enough with this to help you here.

CSRF is only when other domains are trying to send normal HTML forms to your server by getting the browser to send cookies automatically. Frameworks prevent this by inserting unique random strings as hidden fields and checking them again when it's submitted. JWT's in localStorage is safe from this since each domain gets its own separate localStorage area.

But ultimately all this depends on if your service will be using one single domain, in which case httpOnly cookies will be plenty secure and easier to set up, but if you wanna spread your service out on multiple domains like api.domain.com + app.domain.com or add a native app you're forced to store you're JWTs in localStorage or some other native storage area.

Hope this helps!

Brijesh Kalkani
  • 789
  • 10
  • 27
Hjort-e
  • 315
  • 3
  • 9
  • 3
    Thank you for your answer! Since you have recommended local storage, I am interested to know your views on local storage's vulnerability to XSS attack and if there is any safe solution to prevent it. My understanding was modern web frameworks handle cookies in a way that protects them from CSRF attack. – Saptarshi Basu Jan 12 '17 at 14:49
  • 3
    this answer completely ignores the basic principles of OAuth – Hans Z. Jan 12 '17 at 21:40
  • 2
    @Hans Z. enlighten me, which principles... Also OP never mentions using OAuth only JWT – Hjort-e Jan 13 '17 at 08:06
  • 1
    I guess that's my point: why use the same terminology as OAuth ("access token", "refresh token", "bearer token header" - across OP and answer) and try to achieve a similar objective but with a different, proprietary and insecure solution? Just follow the specs. – Hans Z. Jan 13 '17 at 09:04
  • 9
    Then use your answer to tell OP that JWT is a protocol and OAuth is a framework and he should consider building an OAuth solution instead of being snarky... – Hjort-e Jan 13 '17 at 09:13
  • You can use a content security policy (CSP) to improve the security of the JWT in the cookie, however it is *not* a safe place to store this information. Some browsers ignore CSPs and there's always a chance an attacker will be able to get javascript to run on your page.. maybe even through an evil plugin on the users browser which steals cookies..? In the same way you wouldn't store a password in a cookie. – John Hunt Oct 22 '19 at 15:34
  • Cookies are safe depend on you, here it is advised that you trust javascript libraries to be safe, I see it crazy. Who assures you that a javascript library is safe? All of us must use javascript libraries for our projects, and the only thing we can do is trust. Why is localstorage safer when security does not depend on us? – Apyc Jul 16 '20 at 09:02
4

I asked this question two years back and also accepted the answer. However, based on my experience and study in the last two years, I'd like to answer this just in case someone stumbles on this thread with the same question.

The approach mentioned in the question is similar to the "Resource Owner Password Credentials" grant type of OAuth 2.0. However, I think it is better to use the "Authorization Code Grant" type instead and Cookie to store the tokens instead of browser localStorage or sessionStorage. I have detailed my reasons, implementation points, security considerations and references in this StackOverlow answer.

Saptarshi Basu
  • 8,640
  • 4
  • 39
  • 58
3

Like OP I been using resource owner password grant.

I learned so much from Saptarshi Basu's other answer in a different post I think anyone looking into OAuth Code Flow should take a look at it, it has outlined a very solid approach to auth SPA and resource servers. It primarily relies on your backend(resource server) to handle authentication with the auth provider as a private client.

However, I will just add that people looking at implementing authentication with SPA should also consider OAuth Code Flow with PKCE. The main goal of PKCE is to allow public client such as SPA to authenticate directly with auth provider. All PKCE adds, is that when a SPA app initiates authentication, a hashed value is sent to the auth provider when the user is authenticated. And after user authenticate with the authorization provider, it redirects the user back to SPA with that hashed value as well as authorization code. Now, for the next part where the SPA calls auth provider to exchange code for tokens, instead of providing client secret, it has to provide the key that was originally used to create the hashed value. This mechanism guarantees the code cannot be used by someone who intercepted the code, and the SPA doesnt need to store a client secret like a server-side app does.

Now the only thing I'm not certain at this point is which is technically more secure, server-side authentication using standard Code Flow without PKCE or SPA authenticating directly using PKCE? Most resources I could find online currently describes and recommends the latter . However I feel that letting a private server side client handle authentication (as Saptarshi Basu described) might still be more secure. I would love to hear his opinion on this as well.

Oscar Chen
  • 559
  • 5
  • 11
  • 3
    Agreed. PKCE is the way to go and the modern solutions like Okta, Auth0 etc. use PKCE. I didn't know about PKCE when I wrote the other answers. I'll update them for the sake of completeness. Thanks for pointing it out. – Saptarshi Basu Apr 04 '21 at 04:10
1

My understanding of refresh token here is:

Due to the presence of refresh token, we can keep shorter validity period for access token and check frequently (at the expiry of access token) that the user is still authorized to login.

Please correct me if I am wrong.

Assuming you're talking about using JWT as Bearer-token in OAuth (and I would strongly advice to follow the OAuth 2.0 protocol), that's right.

With an additional auth-time (timestamp of authentication) claim in your JWT, you could even drop the second token and sent your access- as a refresh-token (the auth-server could then issue a new access-token if token is valid & auth-time within allowed range)... but sure, it's also good to follow the standard ;)

Anyway, there are certain additional aspects (that tend to get difficult or are even against the fundamental ideas of JWT) you should consider before using JWTs as refresh-token, as this basically means you introduce long-living JWT:

  • do you need to have something like forced user logout/ token revocation by subject (e.g. if user got identified as fraudulent)?
  • do you need to have something like revocation of a specific token (e.g. if a user looses a device)?
  • ...

Dependent on your use-case you should consider all the possible implications, long-living tokens have as they usually require you to introduce some kind of state on your server-side (e.g. to allow revocation/ blacklisting). Keep in mind the beauty and security of the JWT concept lies within JWTs being short-lived.

jbspeakr
  • 466
  • 3
  • 10