How Backend Authentication Works: From Passwords to Sessions and JWTs
The first blog established the difference between authentication and authorization. This one should answer the natural next question: Once a user logs in successfully, how does the backend remember them when HTTP is sta
The first blog established the difference between authentication and authorization. This one should answer the natural next question:
Once a user logs in successfully, how does the backend remember them when HTTP is stateless?
We'll explore how authentication evolved from passwords to session-based authentication and JWTs. I'll keep it moderately detailed, with practical examples but without getting too deep into implementation.
Imagine logging into your favourite application.
You enter your email and password, click Login, and land on your dashboard.
Now you navigate to another page, refresh the browser, or make an API request. Somehow, the application still knows that you're logged in.
But how?
The backend doesn't continuously remember every user who visits the application. In fact, HTTP itself is a stateless protocol.
Each HTTP request is independent. The server doesn't automatically know whether two consecutive requests came from the same user.
So how does an application maintain your login state?
To understand that, let's look at how backend authentication works and how it evolved.
1. It Starts With Passwords
The most familiar form of authentication is username and password authentication.
A user provides their credentials, and the backend verifies them against the stored information.
However, there's an important security consideration: passwords should never be stored as plain text.
Imagine a database containing:
| Password | |
|---|---|
| [email protected] | mypassword123 |
| [email protected] | sam12345 |
If someone gains access to this database, they immediately gain access to users' passwords.
A safer approach is password hashing.
What is password hashing?
Hashing converts an input into a fixed-length representation using a one-way cryptographic function.
For example:
Password: mypassword123
|
v
Hashing
|
v
Stored password hash
When a user logs in, the backend takes the submitted password and verifies it against the stored password hash.
In practice, applications should use dedicated password-hashing algorithms such as Argon2id, bcrypt, or scrypt, which are designed to make password guessing expensive.
They also use salts to prevent identical passwords from producing identical stored hashes.
But password verification solves only the first part of authentication.
Once the credentials are verified, how does the backend remember that user on subsequent requests?
That's where sessions come in.
2. Session-Based Authentication
Session-based authentication is one of the traditional ways of maintaining login state in web applications.
The basic idea is simple:
Instead of sending the user's credentials with every request, the server creates a session and gives the browser a session identifier.
Let's walk through the process.
Step 1: User logs in
The browser sends the user's credentials to the backend.
POST /login
Content-Type: application/json
The backend verifies the credentials.
Step 2: Server creates a session
If the credentials are valid, the backend creates a session for the user.
The session information is stored on the server, commonly in a database or a session store such as Redis.
For example:
Session ID: abc123xyz
User ID: 101
Role: Customer
Expiry: 30 minutes
The session ID uniquely identifies that login session.
Step 3: Server sends a cookie
The backend sends the session ID to the browser using a cookie.
Set-Cookie: session_id=abc123xyz; HttpOnly; Secure; SameSite=Lax
The browser stores this cookie.
Step 4: Browser sends the cookie
When the browser makes another request to the application, it automatically includes the applicable cookie.
GET /dashboard
Cookie: session_id=abc123xyz
Step 5: Backend validates the session
The backend reads the session ID, looks it up in the session store, and checks whether the session is valid.
If it is valid, the backend can identify the associated user and continue processing the request.
The complete flow looks like this:
USER LOGIN
|
v
Verify Credentials
|
v
Create Server Session
|
v
Store Session in DB
|
v
Send Session ID Cookie
|
v
Browser Stores Cookie
|
v
Future Requests
|
v
Cookie Sent to Server
|
v
Validate Session ID
|
v
Identify the User
One important detail: cookies and sessions aren't the same thing.
A session is the server-side record that maintains login state. A cookie is one common way to carry the session identifier between the browser and server.
What happens when a session expires?
Sessions usually have an expiration time.
When a user logs out, the backend can invalidate the session. When a session expires, the backend rejects requests using that session ID.
This approach gives the server direct control over active sessions.
However, it also means the server needs to maintain session state. In applications running across multiple servers, they need a shared session store or another strategy to ensure that requests can be validated consistently.
This brings us to another approach.
3. Token-Based Authentication: Enter JWT
As applications became more distributed, token-based authentication became increasingly popular.
One widely used token format is JWT, which stands for JSON Web Token.
Unlike a traditional session ID, a JWT can contain claims about the user and can be verified using a cryptographic signature.
A typical JWT looks like a long string:
xxxxx.yyyyy.zzzzz
It consists of three parts:
HEADER.PAYLOAD.SIGNATURE
1. Header
The header describes the token type and signing algorithm.
{
"alg": "HS256",
"typ": "JWT"
}
2. Payload
The payload contains claims, which are statements about the user or token.
{
"sub": "101",
"role": "customer",
"iat": 1790000000,
"exp": 1790003600
}
For example:
-
sub: Subject, usually the user's identifier. -
iat: Time when the token was issued. -
exp: Token expiration time.
3. Signature
The signature is generated using the encoded header, payload, and a signing key.
It allows the backend to verify that the token hasn't been modified and that it was signed by a trusted party.
An important distinction: a signed JWT is not automatically encrypted.
Its header and payload are encoded using Base64URL, not hidden. Anyone who obtains the token can generally decode and read its payload.
Therefore, sensitive information such as passwords should never be placed inside an ordinary JWT payload.
How does JWT authentication work?
The flow is slightly different from session-based authentication.
- The user sends login credentials.
- The backend verifies those credentials.
- The backend generates and signs a JWT.
- The token is sent to the client.
- The client includes the token in subsequent API requests.
- The backend verifies the signature and checks the token's validity.
- The backend identifies the user from the token claims.
A typical API request might look like this:
GET /api/profile
Authorization: Bearer <your-jwt-token>
The backend verifies the token before allowing access to the protected resource.
Unlike traditional session authentication, the server doesn't necessarily need to look up a session record for every request.
That's one reason JWTs are useful in distributed systems.
4. Sessions vs JWT: What's the Difference?
Both approaches help applications maintain authenticated state, but they work differently.
| Feature | Session-Based | JWT-Based |
|---|---|---|
| Where state is maintained | Server-side | Claims are carried in the token |
| Client stores | Session ID | Token |
| Server validation | Looks up session | Verifies token |
| Revocation | Invalidate server session | Requires an additional strategy |
| Scalability | Often needs shared session storage | Can simplify distributed verification |
| Main consideration | Session storage and management | Token security and lifecycle |
Neither approach is automatically more secure.
Sessions offer straightforward server-side control over login state.
JWTs can simplify authentication across distributed services, but introduce challenges around token expiration, theft, and revocation.
For example, a JWT may remain valid until it expires even after a user logs out, unless the application implements a revocation mechanism.
Common strategies include short-lived access tokens, refresh-token rotation, and token denylisting.
5. Are Cookies and JWTs Alternatives?
This is a common source of confusion.
Sessions and JWTs describe different ways of maintaining authentication state. Cookies describe a mechanism for storing and sending data between a browser and a server.
They aren't mutually exclusive.
For example, an application can store a JWT inside an HttpOnly, Secure cookie instead of keeping it in JavaScript-accessible storage.
Some important cookie attributes include:
-
HttpOnly: Prevents JavaScript from reading the cookie. -
Secure: Sends the cookie only over HTTPS. -
SameSite: Controls when cookies are sent in cross-site requests.
However, cookie-based authentication still needs appropriate CSRF protection, depending on the application's architecture and cookie configuration.
The important lesson is that choosing JWT doesn't automatically mean avoiding cookies.
6. The Bigger Picture
Let's connect everything we've learned.
When a user logs in:
- The backend verifies their credentials.
- It establishes an authenticated identity.
- It maintains that authentication state using a session or token-based approach.
- The client sends the appropriate session cookie or token with subsequent requests.
- The backend validates it and identifies the user.
- Authorization checks determine whether the user can perform the requested operation.
The authentication process doesn't end when the login request succeeds. The backend needs a reliable way to recognise the user across subsequent requests.
That's the role of sessions and tokens.
Final Thoughts
What looks like a simple login button involves several important backend concepts.
Passwords help establish identity, hashing protects stored credentials, sessions maintain server-side login state, and JWTs provide a way to carry verifiable claims between systems.
There isn't one universal authentication strategy. The right approach depends on the application, its architecture, and its security requirements.
The important thing is understanding what each mechanism does and the trade-offs involved.
What's Next?
We've explored how applications authenticate users and maintain their login state.
But not every request comes from a human sitting behind a browser.
What happens when one application needs to communicate with another? How does a backend securely access an external API?
That's where API Keys and OAuth 2.0 enter the picture.
Next in the series: API Keys and OAuth 2.0: Understanding Authentication Between Applications and Delegated Access.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.