Skip to content

thuinanutshell/comprehensive-auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

How to Build a Comprehensive Authentication Feature Using Different Methods (JWT, Session-based & OAuth)

Python JavaScript React Locust Pytest

Table of Contents

I realized how much I used to struggle in developing the authentication feature for my applications. Such a simple feature caused a lot of headaches, and I am not ashamed to admit that. Because authentication is so integral in many applications. I am curious about how I could create one similar to that. A comprehensive authentication feature will need to have the following functionalities.

Features

Screenshot 2025-07-17 at 8 06 22 PM
Full.Authentication.-.Demo.mp4

Sign Up

Standard Registration

  • Use first/last names, username, email, and password
  • Confirm account via email

OAuth Registration

  • Use a Google Account to sign up

Standard Login

  • Use an identifier (either username or email) and password
  • If the user forgot their password, there should be an option for them to reset their password by:
    • Sending a link to reset their password to their registered email
    • User clicks the link and is redirected to the password reset page
    • They enter their new password
    • Log in again

OAuth Login

  • Use a Google Account sign-in

Profile

  • Users should be able to change their username/email, or password
  • Users should be able to delete their accounts

Session-based Authentication

Explanation

First of all, HTTP is a stateless protocol, which means the client or the server does not keep track of the subsequent requests - each of them is treated as an independent one. Sessions allow the server to associate some information with a client, hence making it easier to retrieve the information for a request. The key difference compared to JWT is that session-based auth has storage both on the server and client side. On the client side, the cookie is stored in session/local storage, and on the server side, it is stored in memory.

Because sessions are stored on the server, it gives more control to the administrators to invalidate a session ID. Also, note that because we are using cookies, we need to pay attention to CORS to allow communication between two different domains.

image

Configuration & Setup

  1. Create a virtual environment inside the session_auth using python3 -m venv .venv and activate it . .venv/bin/activate
  2. Create a .env file with the following environment variables
# Flask Environment
FLASK_ENV=development

# Development variables
DEV_DATABASE_URI=sqlite:///development.db
DEV_SECRET_KEY={your-dev-secret-key}

# Production variables
PROD_DATABASE_URI=sqlite:///production.db
PROD_SECRET_KEY={your-production-secret-key}

JSON Web Token Authentication

Explanation

JSON Web Tokens Components. The format of a JWT is xxxxx.yyyyy.zzzzz, where each part is separated by a dot and represents the header, payload, and signature, respectively. JSON Web Token is a standard that defines a way for parties to transmit data. The security part comes from the digital signature - a secret with the HMAC algorithm (Hash-based Message Authentication Code) or a public/private key pair. So basically, you can also encrypt the token such that the claims are hidden. JWT is used for the following purposes:

  • Authorization: User logged in → request includes JWT → user can access routes, services, and resources associated with that token. I’ve just learned that Single Sign On (SSO) uses JWT! This is when I sign in to my Google account on my laptop, and then I will be automatically logged in to Google’s services like YouTube or Gmail.
  • Information Exchange: Secure information transmission because it makes sure that the senders are who they say they are, thanks to the keys.

image

Configuration & Setup

  1. Create a virtual environment inside the jwt_auth using python3 -m venv .venv and activate it . .venv/bin/activate
  2. Create a .env file with the following environment variables
# Flask Environment
FLASK_ENV=development

# Development variables
DEV_DATABASE_URI=sqlite:///development.db
DEV_JWT_SECRET_KEY={your-dev-secret-key}
DEV_REDIS_URL=redis://localhost:6379/1

# Production variables
PROD_DATABASE_URI=sqlite:///production.db
PROD_JWT_SECRET_KEY={your-production-secret-key}
PROD_REDIS_URL=redis://localhost:6379/0

# Default Redis (fallback)
REDIS_URL=redis://localhost:6379/0
  1. In order to log out the user, we need to initialize a connection to a Redis server running on - you should set the REDIS_URL in your .env file:
def get_redis_client():
    """Get Redis client from current app configuration"""
    from flask import current_app

    redis_url = current_app.config.get("REDIS_URL")
    return redis.from_url(redis_url, decode_responses=True)


@jwt_manager.token_in_blocklist_loader
def check_if_token_is_revoked(jwt_header, jwt_payload: dict):
    """Check if a JWT token is in the blocklist"""
    jti = jwt_payload["jti"]
    redis_client = get_redis_client()
    token_in_redis = redis_client.get(jti)
    return token_in_redis is not None
  • host="localhost": your local machine
  • port=6379: the default Redis port
  • db=0: the default Redis database index
  • decode_responses=True: ensures Redis returns strings (not bytes) When users log out or a token needs to be invalidated before it expires, you can't remove or "cancel" a JWT since it's stateless. So, a common solution is to store a "blocklist" of token identifiers (like the jti claim) in Redis, so your app can check against this list when validating tokens.
  1. To run the app in development mode, follow the commands:
export FLASK_ENV=development
flask --app run init-db
flask --app run migrate-db -m "Initial migration"
flask --app run upgrade-db
flask --app run show-db-info

python3 run.py
  1. To run the app in testing mode, follow the commands:
export FLASK_ENV=testing
python3 run.py

OAuth Authentication

OAuth2 is an authorization protocol designed to allow a website/app to access resources hosted by another web app on behalf of the user. Therefore, it involves granting access to a set of resources (like user data). OAuth also uses tokens (aka access tokens) to represent authorization

image image

Firstly, the user sends an authorization request to the authorization server with ID and secret (also scopes and endpoint if any). Then, the authorization server authenticates the user and verifies the requested scopes and grants authorization to the client. Next, the resource owner and authorization server interact to send an access token to the user. Finally, the user uses the access token to request access to protected resources.

Performance Testing

When I did some research online, there was often this statement saying that JWT is more scalable compared to session-based auth, while the latter offers more control. Then, one question arose: "How can I empirically test if this statement is true or not?" And then I learned about Locust - a load testing tool. To run the locust file in development mode:

cd tests
locust -f locustfile.py --host=http://127.0.0.1:5000

The results from two different setups (number of users 100 vs. 500 with the same ramp-up rate) seemed a bit counterintuitive at first because my expectation was that JWT should be more efficient, both in time and space, compared to session-based. It turned out not to be the case from the experiments (check out the statistics table below). In terms of average size (bytes) and response, session-based authentication turned out to be better! There are a couple of reasons I think why this is the case:

  • Only one Flask server setup makes it easier for session-based auth because there is no need for token parsing or JWT decoding/validation overhead or querying from Redis.
  • The session data is stored in local memory, so it makes the lookup faster in a single-server environment.

I did some research online and found some helpful Reddit and StackOverflow threads discussing the benefits of using JWT in a distributed and microservice system when you want to scale your software horizontally, because there is no need for a shared session store. The key takeaway I got from running this experiment is that, when someone says something is more scalable, we really need to understand what "scalable" exactly means, in which context and applicable to which architecture.

Session Authentication Load Testing (Number of Users = 100 - Peak Concurrency, Ramp up = 10, 2 Minutes)

Screenshot 2025-07-03 at 9 12 35 AM

JWT Authentication Load Testing (Number of Users = 100 - Peak Concurrency, Ramp up = 10, 2 Minutes)

Screenshot 2025-07-03 at 9 23 16 AM

JWT Authentication Load Testing (Number of Users = 500 - Peak Concurrency, Ramp up = 10, 2 Minutes)

Screenshot 2025-07-03 at 9 34 52 AM

Session Authentication Load Testing (Number of Users = 500 - Peak Concurrency, Ramp up = 10, 2 Minutes)

Screenshot 2025-07-03 at 9 38 06 AM

References

[1] https://jwt.io/introduction

[2] https://auth0.com/docs/secure/tokens/json-web-tokens

[3] https://roadmap.sh/guides/session-based-authentication

[4] https://roadmap.sh/guides/session-authentication

[5] https://auth0.com/intro-to-iam/what-is-oauth-2

[6] https://www.reddit.com/r/node/comments/1aox0au/whats_the_ultimate_resource_for_jwt_vs_session/

[7] https://stackoverflow.com/questions/43452896/authentication-jwt-usage-vs-session

[8] http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/

[9] https://github.com/authlib/demo-oauth-client/tree/master/flask-google-login

About

✅ Comprehensive Authentication Feature for Web Apps & Locust Performance Testing

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published