Modern web applications require secure and reliable ways to authenticate users and maintain their session state. Whether building REST APIs, single-page applications (SPAs), or enterprise platforms, choosing the right authentication mechanism is critical for both security and scalability. At PIT Solutions, we guide development teams in selecting and implementing the authentication strategy that fits their architecture.
Two of the most widely used approaches for handling authentication are JWT (JSON Web Tokens) and Cookies. Both techniques are used to maintain user authentication after login, but they operate differently and serve different architectural needs. Selecting the wrong method can introduce security risks such as:
• Session hijacking
• Token theft
• Cross-Site Request Forgery (CSRF)
• Cross-Site Scripting (XSS) exploitation
• Poor scalability in distributed systems
What is Authentication in ASP.NET Core?
Authentication ensures that a user accessing an application is who they claim to be. After successful login, the system must remember the authenticated user for subsequent requests.
Traditionally, this was handled using server-side sessions and cookies, but modern applications—especially APIs and microservices, often use token-based authentication such as JWT.
At PIT Solutions for developers building applications with ASP.NET Core, we recommend understanding the differences between JWT tokens and cookies helps in designing secure authentication systems. Since cookies were the traditional way to manage user sessions, JSON Web Tokens (JWT) emerged to solve several limitations.
What is Cookie-Based Authentication?
Cookies are small pieces of data stored in the user's browser. They are automatically sent with every HTTP request to the server that issued them. The below image depicts traditional way of cookie authentication.
Implementing Cookie Authentication in ASP.NET Core
ASP.NET Core provides built-in cookie authentication middleware.
Step 1: Configure Cookie Authentication
Step 2: Sign In User
The server then sends a secure cookie to the browser.
Limitations of Cookies
1. Server-Side Session Dependency: - Cookies usually store only a session ID, actual data lives on the server. So, Server must keep track of every user session
Example:
- If 10,000 users logged in, server stores 10,000 sessions in memory/database
- Scaling becomes harder
2. Not Ideal for Distributed / Microservices Systems: - Sessions must be shared across servers and requires sticky sessions or shared cache (like Redis)
Example:
- User logs in via Server A
- Next request goes to Server B → session not found
3. CSRF (Cross-Site Request Forgery) Risk: - Cookies are automatically sent with every request. Malicious sites can trigger authenticated actions
Example:
- User logged into banking app
- Visits malicious site → it silently sends request using stored cookie
4. Limited Cross-Domain Usage: - Cookies are tied to a domain. Hard to use across APIs, mobile apps, or different domains
Example:
- Frontend: app.example.com
- API: api.example.com
- Cookie sharing becomes tricky with CORS & SameSite rules
5. Less Flexible for APIs / Mobile Apps: - Cookies work best with browsers. Not ideal for mobile or third-party clients
Example:
- Mobile app calling API → cookies not automatically managed
- Requires extra handling
What is JWT Authentication?
To overcome limitations, PIT Solutions adopted to JWT Authentication. A JWT (JSON Web Token) is a compact, self-contained token used for securely transmitting information between parties as a JSON object. Unlike cookies, JWT tokens typically do not require server-side session storage.
A JWT consists of three parts:
Header
Contains metadata about the token type (JWT) and the signing algorithm used, such as HS256 (HMAC-SHA256, shared secret) or RS256 (RSA, public/private key pair). The algorithm choice is critical — HS256 is simpler for single-server setups; RS256 is preferred in microservices where multiple services verify tokens without sharing a secret.
Payload
Contains the claims — statements about the user and the token, such as user ID, role, issuer, and expiration time (exp). The payload is only Base64-encoded, not encrypted. Never store sensitive data (passwords, card numbers, PII) in the JWT payload — it can be decoded by anyone who has the token.
Signature
The Signature is generated using the header, payload, and a secret key or private key through a hashing algorithm. It ensures the integrity and authenticity of the token, The server uses this signature to verify that the token was not tampered with. A modified payload will produce a different signature and be rejected.
Where to Store JWT Tokens Securely?
One of the most critical decisions when implementing JWT authentication is where to store the token on the client side. The storage mechanism directly impacts the security of your application, especially against attacks like Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF). PIT Solutions evaluates these options carefully for every client engagement.
1. localStorage / sessionStorage
Storing JWTs in localStorage or sessionStorage is a common approach due to its simplicity.
Pros:
- Easy to implement
- Works well with Single Page Applications (SPAs)
- Token persists across requests (and sessions in localStorage)
Cons:
- Highly vulnerable to XSS attacks
- Any malicious JavaScript injected into the page can access and steal the token
- No built-in protection mechanism
Use case: Suitable only for low-risk applications or when strong XSS protections are in place.
2. In-Memory Storage (JavaScript Variables)
The token is stored in a JavaScript variable (e.g., within app state or a service).
Pros:
- Safe from XSS since it is not accessible via persistent storage
- Token is cleared automatically on page refresh or tab close
Cons:
- Token is lost on refresh → requires re-authentication or silent refresh
- Slightly more complex to implement
Use case: Highly secure applications where minimizing attack surface is a priority.
3. HttpOnly Cookies
JWTs can also be stored in cookies with special security flags.
Pros:
- HttpOnly prevents JavaScript access → protects against XSS
- Automatically sent with every request
- Better suited for traditional web apps and secure APIs
Cons:
- Vulnerable to CSRF attacks if not properly configured
- Requires additional protection (e.g., CSRF tokens, SameSite settings)
Use case: Recommended for production applications when combined with proper CSRF protection.
Recommended Approach
For production-grade applications, the most secure and widely recommended approach is (and what PIT Solutions follows in client projects):
- Store JWTs in HttpOnly, Secure cookies
- Always use HTTPS
- Implement CSRF protection mechanisms
In modern architectures, a hybrid approach is often used:
- Access Token → stored in memory (short-lived)
- Refresh Token → stored in HttpOnly cookie (long-lived)
This balances both security and usability, reducing exposure to XSS while maintaining a smooth user experience.
How JWT Token Authentication Works?
A typical JWT authentication flow:
- User submits login credentials to the server
- Server validates credentials against the database
- Server generates a signed JWT containing user claims (ID, role, expiry)
- JWT is returned to the client in the response body
- Client stores the token (more on storage options below)
- Client sends the JWT in the Authorization header on every request
- Server verifies the token signature and expiry — no DB lookup needed
- Server processes the request and returns the response
JWT authentication works using a stateless mechanism, meaning the server does not store session information. After a user successfully logs in, the server generates a signed JWT containing user claims such as ID, role, and expiration time. The client stores this token and sends it in the Authorization header with each request. The server then verifies the token’s signature and validity using the configured secret or public key. If the token is valid, the server processes the request and returns the appropriate response.
Implementing JWT Authentication in ASP.NET Core
Step 1: Install Required Package
Install the JWT authentication package.
Step 2: Configure JWT Authentication
In Program.cs or Startup.cs:
Step 3: Generate JWT Token
Example token generation method:
Access Token vs Refresh Token Strategy
In real-world applications, using a single long-lived JWT is not considered secure. Instead, modern authentication systems—including those built by PIT Solutions—use a combination of access tokens and refresh tokens to balance security and user experience.
Access Token
An access token is a short-lived JWT used to authenticate API requests. This is security measure, if an access token is compromised, the attacker only has a limited window to misuse it.
Characteristics:
- Contains user claims (ID, role, permissions)
- Sent with each request (typically in the Authorization header)
- Short lifespan (e.g., 10–15 minutes)
Refresh Token
A refresh token is a long-lived credential used to obtain new access tokens without requiring the user to log in again.
Characteristics:
- Long lifespan e.g. days or weeks
- Stored securely (preferably in HttpOnly, Secure cookies)
- Sent only to a dedicated refresh endpoint (not with every request)
How the Strategy Works
- User logs in successfully
- Server issues: Short-lived access token AND Long-lived refresh token
- Client: Stores access token (in memory or temporarily) AND Stores refresh token in HttpOnly cookie
- When the access token expires, client calls refresh endpoint, server validates refresh token and issues a new access token
- User continues without interruption
Refresh Token Rotation
To improve security, modern systems implement refresh token rotation. Where each time a refresh token is used, the server issues a new refresh token and immediately invalidates the old one.
This approach significantly improves security because once a refresh token is used, it immediately becomes invalid, preventing anyone from reusing it. It also helps detect potential token theft—if an old refresh token is used again, the system can flag suspicious activity.
Overall, refresh token rotation limits the long-term impact of compromised tokens by ensuring that stolen tokens quickly become useless.
When to Use JWT vs Cookies?
JWT is best suited for scenarios where stateless scalability is a priority. At PIT Solutions, we typically recommend JWT for the following use cases:
- REST APIs consumed by mobile apps, SPAs, or third-party clients
- Microservices architectures where multiple services need to verify identity
- Distributed systems running across multiple servers or containers (Kubernetes, Azure AKS)
- Single Page Applications (React, Angular, Vue.js)
- Cross-domain authentication (e.g., a Vue SPA on app.company.com calling an API on api.company.com)
Because JWT is stateless, servers do not need to store session data. This is a key reason why PIT Solutions favors JWT for distributed and cloud-native architectures.
Cookie-based authentication is ideal when simplicity and server-side control are priorities. PIT Solutions recommends cookies for:
- Traditional server-rendered ASP.NET Core MVC applications
- Applications requiring immediate session revocation (banking, healthcare, admin portals)
- Razor Pages apps where the UI and API share the same domain and origin
- Simpler internal tools or line-of-business apps with modest scale requirements
Cookies work well when authentication and UI are served from the same domain.
Security Best Practices for Authentication
Regardless of which authentication mechanism you choose, these security practices are non-negotiable in a production ASP.NET Core application. At PIT Solutions, our development teams enforce these standards across every project:
- Always use HTTPS — tokens and cookies are plaintext over HTTP and trivially stolen
- Set HttpOnly on cookies — prevents JavaScript from reading the cookie, blocking most XSS token theft
- Set Secure on cookies — ensures cookies are only sent over HTTPS
- Use SameSite=Strict or SameSite=Lax — significantly reduces CSRF risk
- Keep JWT expiry short (15–60 minutes) and use refresh token rotation
- Never store sensitive PII or passwords in JWT payloads
- Use RS256 (asymmetric) signing in microservices — services can verify without sharing a secret
- Validate every token on every request — never cache or skip validation
- Protect against CSRF when using cookie-based auth with ASP.NET Core antiforgery middleware
- Log authentication failures and monitor for unusual patterns (brute force, token replay)
JWT vs Cookies Authentication: Key Differences
| Feature | JWT Authentication | Cookie Authentication |
|---|---|---|
| Storage | Client-side token (local storage, session storage, or cookie) | Stored in browser cookies |
| Server Session | Stateless (no session stored on server) | Requires server session management |
| Stateless | Yes | No |
| Automatic Sending | No – must manually add in Authorization header | Yes – browser sends automatically |
| CSRF Protection | Not built-in (needs additional protection) | Built-in protection |
| CSRF Risk | Lower | Higher |
| Session Management | Manual via token expiration & refresh tokens | Automatic session handling |
| Scalability | High (ideal for microservices & distributed systems) | Lower due to server session storage |
| API + SPA Support | Designed for APIs, SPAs, and mobile apps | More cumbersome for APIs |
| Token / Data Size | Larger (contains encoded claims) | Smaller |
| Best Use Case | APIs, SPAs, mobile applications | Traditional server-rendered web apps |
Conclusion
Authentication is a foundational aspect of building secure modern applications, and the choice between cookies and JWT should be driven by architectural needs rather than preference. Cookie-based authentication, with its server-managed sessions, remains a strong fit for traditional, server-rendered applications where tight session control and simplicity are priorities. However, its stateful nature and scalability limitations make it less suitable for distributed and API-driven environments.
JWT-based authentication, on the other hand, offers a stateless and highly scalable approach, making it the preferred choice for APIs, microservices, and single-page applications. When combined with best practices such as short-lived access tokens, secure storage, and refresh token rotation, JWT provides both flexibility and robust security.
PIT Solutions recommends JWT authentication for modern microservices and SPA-based architectures. For traditional web applications, cookies remain an excellent, proven solution. For the best balance of security, scalability, and user experience, consider a hybrid approach — short-lived access tokens paired with refresh tokens stored in HttpOnly cookies.