Cookie-Based Authentication: A Simple Guide for Secure Sessions
Straightforward guide to implementing secure cookie-based authentication with proper session management.

Authentication is the process of verifying the identity of a user, device, or entity in a system. It ensures that the person or system accessing resources is who they claim to be. The main goal is to protect systems and data from unauthorized access.
We can do authentication in multiple ways -- with libraries like Next-Auth, Clerk, Lucia, Auth0, Passport, etc. But let's focus on 2 custom authentication systems: JWT + Local Storage and Cookies.
JWT + Local Storage
JWT (JsonWebToken) is a way to transmit information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. It contains a header, payload, and a signature containing various aspects of the information we want to transmit safely.
Here is an example of JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cplain text
localStorage is a web storage feature allowing web applications to store data on the browser with no expiration time -- data is persisted even after the browser is closed and reopened until it is cleared manually.
In a client-side application using localStorage for authentication, we store the token received upon signing in/up in localStorage, then use it when sending requests to protected routes by setting the token in the request headers:
axios.get('https://xyz.com/api/dummy', { headers: { Authorization: localStorage.getItem('token') } })javascript
This is a good enough approach, but there is a problem: Cross Site Scripting (XSS).
Cross Site Scripting is a type of security vulnerability where a malicious user injects script on web pages viewed by other users. These scripts execute in the context of the victim's browser, potentially allowing the attacker to access sensitive information like authentication tokens.
If a token is stored in localStorage, it can easily be stolen because localStorage presents an entire synchronous API:
localStorage.getItem('token')javascript
You don't want this to happen. One way to mitigate this threat is to use cookies for transmitting authentication tokens.
Cookies
Cookies are small pieces of data stored on the user's web browser while browsing. They provide a reliable mechanism for websites to store user preferences, authentication cookies, and more.
Reasons to prefer cookies over localStorage:
- Automatic Inclusion: Cookies are sent with every subsequent request once set on the client side -- you don't have to explicitly set a header for every API call.
- Server Side Rendering Compatibility: localStorage doesn't work with Next.js without compromising on its offerings, because localStorage is a client-side synchronous API whereas Next.js components are pre-rendered on the server side by default.
- Security Features: Cookies have an expiry set to them and can be restricted to only HTTPS transmission and certain domains.
Types of Cookies
- Persistent -- Cookies that stay on your browser even after you close the window. They only expire when they reach their expiry time/date or the user manually deletes them.
- Session -- Cookies that stay on your browser for the duration of a session, and get removed automatically once the session finishes.
Properties of Cookies
- Secure: Ensures the cookie is sent only over HTTPS connections.
- HttpOnly: Ensures cookies aren't accessed by client-side scripts, mitigating XSS attacks.
- Expires and Max-Age: Specifies the expiration date and maximum age of the cookie after which it is deleted.
- Domain and Path: Specifies the domain and path within the domain for which the cookie is valid.
- SameSite: Controls whether cookies are sent with cross-site requests, helping to prevent CSRF attacks.
SameSite settings:
- Strict: Cookies are only sent in a first-party context. This prevents cookies from being sent with requests from third-party sites, effectively blocking CSRF attacks.
- None: Cookies are sent with all requests, including cross-origin requests. For None to work, the Secure attribute must also be set.
- Lax: Cookies are sent with top-level navigation requests and some GET requests but not with embedded requests like images or frames from third-party sites and POST requests. This provides a balance between security and preventing most CSRF attacks.
CSRF Attacks
A CSRF attack occurs when an attacker tricks a user's browser into making an unwanted request to a different site at which the user is authenticated. This can happen because browsers automatically include cookies with every request to the server they are associated with.
For example:
- A user is logged into their bank website and has an active session cookie.
- The user visits a malicious site while still logged into the bank website.
- The malicious site contains a form that automatically submits a request to the bank's transfer funds endpoint, exploiting the user's active session.
- The bank server processes the request because it is sent with the user's session cookie, leading to an unauthorized transfer of funds.
Conclusion
While both JWT with local storage and cookie-based authentication provide mechanisms for maintaining secure sessions, cookies offer a more secure alternative, particularly against Cross-Site Scripting (XSS) attacks. By leveraging properties such as HttpOnly, Secure, and SameSite, cookies mitigate common vulnerabilities and integrate seamlessly with server-side rendering frameworks like Next.js.
Choosing cookies for authentication can enhance the security of your web applications, protecting sensitive user information and ensuring robust session management. Adopting the right authentication strategy tailored to your specific needs is crucial for maintaining the integrity and security of your systems.