Skip to content

codeAs-sohail/Production-Auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

🔐 Authentication System (NoteNest Learning)

A robust, token-versioned authentication project born from real-world development challenges.


📖 The Backstory

While working on my recent NoteNest project, I ran into significant hurdles related to authentication and authorization.

At the time, I was manually decoding refresh tokens and handling all validation logic myself. This approach made the project:

  • Unnecessarily complex
  • Harder to maintain
  • Bloated with excessive lines of code

Additionally, I noticed that Django’s default authentication system works best with its built-in user model. Modifying it (adding fields, changing behavior) required extra effort, workarounds, and custom handling.

So, I decided to rebuild the authentication system from the ground up—properly.


💡 What I Changed

To modernize and secure the setup, I implemented several structural improvements:

  • Replaced the default user model with a custom user model.
  • Adopted JWT-based authentication to eliminate manual token handling.
  • Leveraged built-in permission classes like IsAuthenticated.
  • Centralized all security logic using a strictly scoped custom authentication.py.
  • Introduced Token Versioning to fix a critical session security vulnerability.

⚠️ The Real Problem I Faced

Standard JWT implementation features a major flaw. Even after a user clicks "Logout":

  • Refresh Token gets successfully blacklisted.
  • Access Token remains fundamentally valid until its time expiry runs out.

That means a user (or an attacker with a stolen token) can still successfully call private APIs even after logging out. This was the biggest security hole in the project.


🛠️ My Solution: Token Versioning

To fix this decisively, I introduced a token_version field to the custom user model.

This integer value is:

  1. Stored dynamically in the database.
  2. Embedded instantly inside the JWT token's payload upon generation.

Now, every single API request performs a strict check:

👉 "Does the token_version inside the token match the token_version stored in the database?"


🔄 Token Version Flow (My Exact Logic)

Here is a step-by-step breakdown of how the versioning works across a session lifecycle:

1️⃣ Register / Login 
   → token_version = 0 (stored in DB)
   → Token payload embeds version '0'
   🟢 DB = 0, Token = 0 → MATCH ✅ (Request Allowed)

2️⃣ Logout 
   → DB version increments to 1
   → The attacker's old token still embeds version '0'
   🔴 DB = 1, Token = 0 → MISMATCH ❌ (Request Blocked Instantly)

3️⃣ Login Again 
   → New token is created
   → New token payload embeds version '1'
   🟢 DB = 1, Token = 1 → MATCH ✅ (Request Allowed)

4️⃣ Logout Again 
   → DB version increments to 2
   → The previous token still embeds version '1'
   🔴 DB = 2, Token = 1 → MISMATCH ❌ (Request Blocked Instantly)

5️⃣ Login Again 
   → New token is created embedding version '2'
   🟢 DB = 2, Token = 2 → MATCH ✅ (Request Allowed)

👉 The Impact:

  • Old tokens die instantly.
  • We no longer have to wait for the standard token expiry window.
  • Logout becomes truly, cryptographically secure.

🔌 APIs I Implemented

Each API is kept extremely clean and heavily utilizes serializers for validation and structured response handling:

  • 🟢 POST /register/ → Creates a new user and natively returns fresh access/refresh tokens.
  • 🟢 POST /login/ → Verifies user credentials and generates new tokens encoding the latest version.
  • 🔴 POST /logout/ → Blacklists the refresh token and increments the user's token_version.
  • 🔵 GET /profile/ → Returns the user's secure profile data (strictly protected, requires valid auth).

🔐 authentication.py (The Engine)

Instead of checking tokens manually in every view, I offloaded everything to a custom authentication class.

It handles validation under the hood before the view ever runs. It:

  1. Reads the token from the request header.
  2. Unpacks and mathematically validates the signature.
  3. Compares the packed token_version with the target Database.
  4. Allows or blocks the request autonomously.

This keeps the codebase incredibly clean, DRY, and completely reusable.


⚙️ settings.py (Global Setup)

  • Authentication is handled globally using the custom JWT authentication class.
  • Permissions are controlled fundamentally using IsAuthenticated.
  • Public-facing APIs (like Login or Register) intentionally override this using AllowAny.

Because of this specific design, I never have to write raw authentication logic repeatedly.


✅ Final Result

By implementing this architecture:

  • 📈 Authentication became drastically simpler.
  • 🧹 Code became cleaner and shorter.
  • 🛡️ Security improved significantly.
  • 🔒 Logout now actually invalidates all old tokens instantly across all devices.

📌 Why I Made This

Many developers learn by reading real projects on GitHub. This repository represents an implementation where I solved a real-world architectural problem I personally faced.

If you're building a JWT-based framework stack, borrowing this approach can save you days of confusion, spaghetti code, and security bugs.


If you'd like to see more, feel free to request API request/response examples or explore the codebase firsthand! 🚀

About

A clean authentication system built with Django REST using JWT, custom user model, and token versioning for better security.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages