Building Authentication from Scratch: JWT vs Session-Based Auth

Authentication is one of those things every web developer has to implement but rarely understands deeply. Most of us just copy a tutorial and hope for the best. Let’s fix that.

Session-Based Authentication

The traditional approach. Here’s how it works:

  1. User sends username and password
  2. Server verifies credentials, creates a session object stored in memory or a database
  3. Server sends back a session ID as a cookie
  4. Browser automatically sends this cookie with every subsequent request
  5. Server looks up the session ID to identify the user
# Server-side session creation (Flask example)
from flask import session

@app.route('/login', methods=['POST'])
def login():
    user = verify_credentials(request.json)
    if user:
        session['user_id'] = user.id
        session['role'] = user.role
        return jsonify({'message': 'Logged in'})
    return jsonify({'error': 'Invalid credentials'}), 401

JWT (JSON Web Token) Authentication

The modern approach for APIs:

  1. User sends username and password
  2. Server verifies credentials, creates a signed JWT containing user info
  3. Server sends the JWT to the client
  4. Client stores it (localStorage or cookie) and sends it in the Authorization header
  5. Server verifies the JWT signature – no database lookup needed
import jwt
from datetime import datetime, timedelta

SECRET = 'your-secret-key'

def create_token(user):
    payload = {
        'user_id': user.id,
        'role': user.role,
        'exp': datetime.utcnow() + timedelta(hours=24)
    }
    return jwt.encode(payload, SECRET, algorithm='HS256')

def verify_token(token):
    try:
        return jwt.decode(token, SECRET, algorithms=['HS256'])
    except jwt.ExpiredSignatureError:
        return None

Honest Comparison

Session Advantages

  • Server can revoke sessions instantly (logout works immediately)
  • Session data stays on the server (more secure)
  • Cookies are sent automatically by the browser
  • Simpler mental model

JWT Advantages

  • Stateless – server doesn’t need to store anything
  • Works across different domains and services
  • Great for microservices where multiple services need to verify identity
  • Mobile apps and SPAs can store tokens easily

JWT Disadvantages (That Articles Don’t Mention)

  • You can’t truly “log out” – the token is valid until it expires
  • Storing tokens in localStorage is vulnerable to XSS
  • Token size is larger than a session cookie
  • Refresh token logic adds significant complexity

My Recommendation

Server-rendered app with one backend? Use sessions. They’re simpler and more secure.

SPA with a separate API backend? Use JWTs in httpOnly cookies (not localStorage).

Microservices? JWTs with short expiration times and refresh tokens.

Security Regardless of Approach

  • Always use HTTPS
  • Hash passwords with bcrypt or Argon2
  • Implement rate limiting on auth endpoints
  • Use HttpOnly, Secure, and SameSite cookie attributes
  • Add CSRF protection for cookie-based auth

Don’t roll your own crypto. Use established libraries (passport.js, Flask-Login, Spring Security). Authentication is one area where “boring and proven” beats “clever and custom” every time.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top