Cookie, session, token, JWT, attacks, where to store token, security concerns? Everything you need to know is here
Authentication in a single page application (SPA) involves several patterns with pros and cons. This article will list the main important concepts and best practices to know and have in mind when dealing with user authentication, especially in this common architecture:
X-Powered-Bythat says what kind of server you use in the response header. You may use Helmetjs if you are operating ExpressJS.
# List security breaches npm audit # Upgrade of minor and patch version following your version ranges in package.json yarn outdated yarn upgrade # Interactive upgrade of minor and patch version following your version ranges in package.json yarn upgrade-interactive # List outdated dependencies including major version yarn upgrade-interactive --latest # Same with npm npm outdated npm update # Tool for upgrading to major versions (with potential breaking changes) npm install -g npm-check-updates ncu
There are 2 main authentication mechanisms (you will see later that we can combine them) to identify a client on a REST API:
Authentication is the act of identifying a user/client.
A bearer token is a value that goes into the Authorization header of any HTTP requests. It is not automatically stored anywhere, it has no expiry date and no associated domain. It’s just a value:
GET https://www.example.com/api/users Authorization: Bearer my_bearer_token_value
curl command line, it could be
curl -s -X GET "https://www.example.com/api/users" -H "Authorization: Bearer my_bearer_token_value"
To have a stateless application, we can use JWT for our token format. Basically, a JWT contains 3 parts:
JWT is a cryptographically secure means of exchanging information that make stateless authentication possible. The signature certifies that the payload has not been modified (i.e. that is not compromised) thanks to symmetric or asymmetric (RSA) signature. The header contains the format and public key address to verify the signature (for asymmetric).
We have to manually store the JWT in the clients (memory, local/session cookie, local storage, etc…).
It is not recommended to store the JWT in the browser local storage:
Storing JWT in session cookie may be the solution, we will talk about that later.
To go further: https://auth0.com/docs/security/store-tokens
<img src=x onerror="javasc& #0000114ipt:ale� 00114t('XSS')">
very difficult to detect and mitigate a compromised web dependency served by a public CDN.
or from the server using an HTTP Response header:
Set-Cookie: my_cookie_name=my_cookie_value // HTTP Response Header
The web browser automatically sends cookies with every request to the cookie’s domain.
GET https://www.example.com/api/users Cookie: my_cookie_name=my_cookie_value
In most (stateful) use cases, a cookie is used to store a session ID. The session ID is managed by the server (creation and timeout). We talk about stateful as the server needs to manage a state on the server whereas a JWT token is stateless.
There are 2 kinds of cookies (source):
Session cookies: The cookie is deleted when the client shuts down because it doesn’t specify an Expires or Max-Age directive. However, web browsers may use session restoring, which makes most session cookies permanent, as if the browser was never closed. The session timeout must be handled on the server side.
Instead of expiring when the client closes, permanent cookies expire at a specific date (Expires) or after a specific length of time (Max-Age).
Server cookies can be configured with several options:
They are automatically stored in the web browser with an expiry date (optional) and associated domain.
Another example of CSRF: let’s assume that the user, while he is still logged in to facebook.com, visits a page on bad.com. Now, bad.com belongs to an attacker where he has coded the following on bad.com:
To mitigate XSS, the HttpOnly option must be set on the cookie.
To mitigate CSRF, the SameSite option must be set on the cookie. The SameSite option is not supported by all browsers so it will not prevent all CSRF attacks. Some other mitigation strategies (that can be combined) can be used:
Let’s summarize what we are looking for our authentication mechanism on our server API:
What about putting a JWT inside a cookie to get the best of both worlds?
JWT can be updated on each request seamlessly by the server as the new one will be in the cookie response and automatically stored by the browser. This way, the expiration date of the JWT can be put back.
To limit CSRF, mutations should never be done using a GET query, use PUT or POST. Mutations with high-security concerns should ask user credentials again, for instance, the change email mutation should ask the user password to validate the change. The temporary cookie could also embed a random number that is read by JS and submit in a hidden form field as long with the form data. The server has to check if the random number in the cookie matches the value from the form data.
The authentication flow for our SPA is now the following:
If you are using a single httpOnly cookie, the SPA should make an API call, for instance, //backend/api/me to know who is the current user and get an unauthorized error if the authentication cookie (containing the JWT) is missing or invalid.
STEP 2 — Option 1: the /login page on the front end asks for user credentials (login/password) and then posts them on the backend API using an AJAX request. The AJAX response will set the authentication cookie with a JWT inside.
STEP 2 — Option 2: the /login page provides an OpenID authentication using an OAuth flow. For an authorization code grant flow, the /login should redirect the whole browser window to
Feel free to discuss on twitter.