When you code multipage applications with Rails or Laravel framework, you probably don’t pay a lot of attention to the authentication process. However, if you’re like me, and the logic behind it piques your interest, you can switch to NodeJS.
Start writing REST APIs for Single Page Application (SPA) and trying out the JWT token-based authentication using the httpOnly cookie flag. Here’s an overview of the authentication process using JWT tokens to make it easier for you.
The Storing of Session Data: Stateful vs. Stateless Approach
The main difference between SPA and Multipage applications regarding authentication is whether session data is stored on the server or not. In other words, multipage applications have a so-called stateful authentication approach and SPA have a stateless one.
The REST API is the most widely used SPA data layer. One of the architectural restrictions of its design is that each request must hold all required data from the application state in order to change the server's resource state. The "ST" in REST – REpresentational State Transfer – refers to the fact that you are constantly transferring a state between the server and the client over the HTTP protocol.
JWT Token – The Key Component
Stateless REST API requires storing session data on the client-side. A key component is a JWT token that holds authentication data that can be confidentially transmitted between clients.
In SPA, developers commonly store the JWT token in the browser’s local storage and include it in an authorization header for each request, possibly leading to security threats. Because local storage is readable from JavaScript, a simple cross-site-scripting attack or XSS could read the JWT token and open doors to impersonate a user.
Therefore, we need to find another solution to store the JWT token on the browser.
Bring Back the Good Old Cookies
Are you considering putting the JWT token in a cookie? Reconsider your position. Since cookies are readable by JavaScript, you'd be facing the same problem as placing the token in the browser's local storage. However, you can prevent JavaScript from accessing a cookie. For example, have a server set a cookie with an httpOnly flag and JavaScript won’t be able to read it.
Nonetheless, your problems are not over yet. With cookies, old security issues become relevant again. When you send a request, all cookies from the cookie domain get sent as well. Meaning if a victim performs a request to the API from the attacker’s malicious site, cookies, like session cookies, will also be sent. This security risk is known as cross-site request forgery (CSRF).
To mitigate CSRF risk, we can:
- Use a custom request header, such as “x-requested-with” which will be checked on the server-side. The header will work because we can only use JavaScript to add a custom header on Ajax requests and only within its origin.
- Use a SameSite cookie flag, meaning the cookie will only be sent if the site for the cookie matches the site currently shown in the browser's URL bar.
The Final Step
Can you imagine what the main problem with this approach seems to be? A browser client cannot read data supplied via the JWT token. This is especially crucial when the SPA needs to manipulate the app's user interface using session data. For example, some program elements should not be visible to certain user roles.
JWT token consists of three parts:
- payload
- signature
- header
If the payload has data that the front-end needs, we have to provide it somehow. A simple solution is splitting the JWT token into two cookies:
- one holding payload
- one with signature and header data
Payload cookie should have httpOnly flag set to false and signature.header cookie must have httpOnly flag set to true. Here is a diagram that shows the whole flow.
The key advantage of using this type of authentication system is improving the app's overall security. Another less obvious benefit is that the front-end developer will have a far better experience. He can now focus solely on how he will read the content of the payload cookie because he no longer needs to care about session data storage.
To summarize the overview of JWT and cookie authentication in SPAs, we should point out that we've done the majority of the work on the only place where you should – the back-end.