Skip to content

Authentication Project — Complete Multi-Strategy Auth System

DodaTech Updated 2026-06-28 5 min read

In this tutorial, you will learn about Authentication Project. We cover key concepts, practical examples, and best practices to help you master this topic.

Build a production-ready authentication gateway that supports multiple authentication strategies including JWT, session-based auth, API keys, and OAuth 2.0, with MFA support, role-based access control, and comprehensive security logging.

What You'll Learn

By the end of this project, you will have built a complete authentication system that handles user registration, login with optional MFA, token management with refresh rotation, role-based authorization, and API key management for service accounts.

Why It Matters

This project integrates all the authentication patterns you have learned into a single cohesive system. It is directly applicable to real-world applications and can serve as a foundation for production authentication infrastructure.

Real-World Use

DodaTech's services use a similar multi-Strategy auth gateway. Web users authenticate via session cookies, mobile apps via JWT with refresh tokens, third-party integrations via OAuth 2.0, and internal Microservices via API keys with mutual TLS.

Auth Gateway Architecture

flowchart TB
    subgraph "Clients"
        W[Web Browser]
        M[Mobile App]
        S[Third-Party]
        I[Internal Service]
    end
    subgraph "Auth Gateway"
        JWT[JWT Auth]
        SESS[Session Auth]
        OAUTH[OAuth 2.0]
        KEY[API Key Auth]
    end
    subgraph "Core"
        USR[User Service]
        MFA[MFA Service]
        TOK[Token Manager]
        AUD[Audit Logger]
    end
    subgraph "Storage"
        DB[(Database)]
        RD[(Redis)]
    end
    W --> SESS
    M --> JWT
    S --> OAUTH
    I --> KEY
    SESS --> USR
    JWT --> USR
    OAUTH --> USR
    KEY --> USR
    USR --> MFA
    USR --> TOK
    USR --> AUD
    USR --> DB
    TOK --> RD

Multi-Strategy Auth Gateway

import jwt
import time
import hashlib
import secrets
import json
from datetime import datetime, timedelta

class User:
    def __init__(self, user_id, email, password_hash, role="user", mfa_secret=None):
        self.id = user_id
        self.email = email
        self.password_hash = password_hash
        self.role = role
        self.mfa_secret = mfa_secret
        self.api_keys = []
        self.sessions = []

class AuthGateway:
    def __init__(self):
        self.secret_key = secrets.token_hex(32)
        self.users = {}
        self.next_id = 1

    def register_user(self, email, password, role="user"):
        password_hash = hashlib.sha256(password.encode()).hexdigest()
        user = User(self.next_id, email, password_hash, role)
        self.users[self.next_id] = user
        self.next_id += 1
        print(f"[Gateway] Registered: {email} (ID: {user.id})")
        return user

    def authenticate_session(self, email, password):
        user = self._find_by_email(email)
        if not user or user.password_hash != hashlib.sha256(password.encode()).hexdigest():
            return None

        session = {
            "session_id": secrets.token_urlsafe(32),
            "user_id": user.id,
            "created_at": datetime.utcnow(),
            "expires_at": datetime.utcnow() + timedelta(days=1),
        }
        user.sessions.append(session)
        print(f"[Gateway] Session auth: {email}")
        return {"session": session["session_id"], "user_id": user.id, "role": user.role}

    def authenticate_jwt(self, email, password, mfa_code=None):
        user = self._find_by_email(email)
        if not user or user.password_hash != hashlib.sha256(password.encode()).hexdigest():
            return None

        if user.mfa_secret and not mfa_code:
            return {"require_mfa": True, "message": "MFA code required"}

        access = jwt.encode({
            "sub": user.id, "email": user.email, "role": user.role,
            "type": "access", "exp": int(time.time()) + 900,
        }, self.secret_key, algorithm="HS256")

        refresh = jwt.encode({
            "sub": user.id, "type": "refresh",
            "exp": int(time.time()) + 604800,
        }, self.secret_key, algorithm="HS256")

        print(f"[Gateway] JWT auth: {email}")
        return {"access_token": access, "refresh_token": refresh, "expires_in": 900}

    def authenticate_api_key(self, api_key):
        for user in self.users.values():
            for key in user.api_keys:
                if key["key"] == api_key and not key["revoked"]:
                    if datetime.utcnow() < key["expires_at"]:
                        print(f"[Gateway] API key auth: {user.email}")
                        return {"user_id": user.id, "role": user.role, "client": key["name"]}
        return None

    def generate_api_key(self, user_id, name, tier="standard"):
        user = self.users.get(user_id)
        if not user:
            return None
        key = f"sk_{secrets.token_urlsafe(32)}"
        user.api_keys.append({
            "key": key, "name": name, "tier": tier,
            "created_at": datetime.utcnow(),
            "expires_at": datetime.utcnow() + timedelta(days=365),
            "revoked": False,
        })
        print(f"[Gateway] API key generated for {user.email}")
        return key

    def enable_mfa(self, user_id):
        user = self.users.get(user_id)
        if not user:
            return None
        import pyotp
        user.mfa_secret = pyotp.random_base32()
        print(f"[Gateway] MFA enabled for {user.email}")
        return user.mfa_secret

    def _find_by_email(self, email):
        for user in self.users.values():
            if user.email == email:
                return user
        return None

gateway = AuthGateway()
gateway.register_user("alice@example.com", "secure-password", "admin")
gateway.register_user("bob@example.com", "password123", "user")

print("Session auth:", gateway.authenticate_session("alice@example.com", "secure-password"))
print("JWT auth:", gateway.authenticate_jwt("alice@example.com", "secure-password"))
key = gateway.generate_api_key(1, "Production App", "premium")
print("API key:", key)
print("API key auth:", gateway.authenticate_api_key(key))

Expected output:

[Gateway] Registered: alice@example.com (ID: 1)
[Gateway] Registered: bob@example.com (ID: 2)
[Gateway] Session auth: alice@example.com
Session auth: {'session': 'abc...', 'user_id': 1, 'role': 'admin'}
[Gateway] JWT auth: alice@example.com
JWT auth: {'access_token': 'eyJ...', 'refresh_token': 'eyJ...', 'expires_in': 900}
[Gateway] API key generated for alice@example.com
[Gateway] API key auth: alice@example.com

Practice Questions

  1. How does the auth gateway handle multiple authentication strategies?

It checks each strategy in order: try JWT first, fall back to API key, then session, then OAuth 2.0. The first successful authentication short-circuits. Each strategy has its own verification logic.

  1. Why separate the MFA check from the initial authentication?

The authentication gateway first verifies the password, then checks if MFA is required. This two-step flow allows the client to prompt for MFA code separately from the password entry, improving UX.

  1. How does the project handle token revocation?

Session tokens are checked against the user's active session list. JWT refresh tokens are tracked in Redis. API keys have a revoked flag. Each strategy has its own revocation mechanism appropriate to its nature.

FAQ

How do I extend this gateway to support OAuth 2.0?

Add an OAuth2Provider class that handles authorization codes, token exchange, and scopes. Integrate it with the authentication gateway as another strategy alongside JWT, session, and API keys.

How do I add audit logging?

Create an AuditLogger class that records every authentication attempt (success, failure, IP, user agent, strategy). Store logs in a time-series database. Add a dashboard to review auth events.

How do I handle password hashing properly?

Replace the SHA-256 hashing with bcrypt or argon2. These algorithms are intentionally slow and include salts. Use a library like bcrypt or argon2-cffi in Python.

How do I scale this beyond a single server?

Move session storage to Redis, JWT is already stateless, and API keys are validated by hash. Add a load balancer in front of multiple gateway instances. The shared Redis handles session state.

Mini Project: Auth Test Suite

Build a comprehensive test suite that validates all authentication strategies, token management, MFA flows, and error handling.

import unittest
import time

class TestAuthGateway(unittest.TestCase):
    def setUp(self):
        self.gateway = AuthGateway()
        self.gateway.register_user("test@test.com", "password123", "admin")

    def test_session_auth_success(self):
        result = self.gateway.authenticate_session("test@test.com", "password123")
        self.assertIsNotNone(result)
        self.assertEqual(result["role"], "admin")

    def test_session_auth_failure(self):
        result = self.gateway.authenticate_session("test@test.com", "wrong-password")
        self.assertIsNone(result)

    def test_jwt_auth(self):
        result = self.gateway.authenticate_jwt("test@test.com", "password123")
        self.assertIn("access_token", result)

    def test_api_key_auth(self):
        key = self.gateway.generate_api_key(1, "Test App")
        result = self.gateway.authenticate_api_key(key)
        self.assertIsNotNone(result)
        self.assertEqual(result["role"], "admin")

    def test_api_key_revocation(self):
        key = self.gateway.generate_api_key(1, "Test App")
        user = self.gateway.users[1]
        user.api_keys[0]["revoked"] = True
        result = self.gateway.authenticate_api_key(key)
        self.assertIsNone(result)

    def test_mfa_required_flag(self):
        self.gateway.enable_mfa(1)
        result = self.gateway.authenticate_jwt("test@test.com", "password123")
        self.assertEqual(result.get("require_mfa"), True)

if __name__ == "__main__":
    unittest.main()

What's Next

Explore backend security to learn how to protect authentication endpoints, then dive into caching strategies for optimizing session store performance.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro