LEVEL 4

Authentication & Authorization

Intermediate

🎯 Learning Objectives

🔐 Authentication vs Authorization

These two concepts are fundamental to security but serve different purposes:

🔑 Authentication (AuthN)

"Who are you?"

  • Verifying identity
  • Proof of identity
  • Login process
  • Credentials check

Examples:

  • Password login
  • Biometrics
  • 2FA/MFA
  • Certificate authentication

📋 Authorization (AuthZ)

"What can you do?"

  • Determining permissions
  • Access control
  • Policy enforcement
  • Privilege check

Examples:

  • Role-based access
  • File permissions
  • API rate limiting
  • Feature flags

🔒 Password Security

Proper password storage is critical. Never store passwords in plain text!

⚠️ Never Do This

# VULNERABLE - Storing plain text passwords
# This is extremely dangerous!

users = {
    "john": "password123",  # NEVER!
    "jane": "qwerty",     # NEVER!
}

# If database is compromised, ALL passwords are exposed!
# Attackers can use these passwords on other sites too!

✅ Secure Password Hashing with bcrypt

import bcrypt

def hash_password(password: str) -> str:
    """Hash a password using bcrypt."""
    # Generate salt (work factor 12 is recommended)
    salt = bcrypt.gensalt(rounds=12)
    # Hash the password
    hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
    return hashed.decode('utf-8')

def verify_password(password: str, hashed: str) -> bool:
    """Verify a password against a hash."""
    return bcrypt.checkpw(
        password.encode('utf-8'),
        hashed.encode('utf-8')
    )

# Example usage
password = "MySecureP@ssw0rd!"
hashed_password = hash_password(password)
print(f"Hashed: {hashed_password}")
# Output: $2b$12$LQv3c1yq.... (60 char string)

# Verify
is_valid = verify_password(password, hashed_password)
print(f"Valid: {is_valid}")  # True

✅ Secure Password Hashing with Argon2

# Argon2 - Winner of Password Hashing Competition
# Recommended for new projects

import argon2

def hash_password_argon2(password: str) -> str:
    """Hash password using Argon2."""
    ph = argon2.PasswordHasher(
        time_cost=2,      # Number of iterations
        memory_cost=65536, # Memory usage in KB
        parallelism=1,   # Number of threads
        hash_len=32,     # Length of hash
        salt_len=16      # Length of salt
    )
    return ph.hash(password)

def verify_password_argon2(password: str, hash: str) -> bool:
    """Verify password against Argon2 hash."""
    ph = argon2.PasswordHasher()
    try:
        ph.verify(hash, password)
        return True
    except:
        return False

# Example
hashed = hash_password_argon2("secure_password")
print(hashed)
# $argon2id$v=19$m=65536,t=2,p=1$...

🛡️ Password Security Best Practices

  • Use strong hashing algorithms - bcrypt, Argon2, or scrypt
  • Use unique salts - Prevents rainbow table attacks
  • Implement proper work factors - Balance security vs performance
  • Enforce strong passwords - Minimum length, complexity requirements
  • Implement rate limiting - Prevent brute force attacks
  • Use multi-factor authentication (MFA) - Add extra layer of security

📱 Session Management

HTTP is stateless - sessions maintain user state across requests.

Secure Session Implementation

import secrets
import hashlib
from datetime import datetime, timedelta

class SecureSession:
    def __init__(self):
        self.sessions = {}
        self.secret_key = secrets.token_hex(32)
    
    def create_session(self, user_id: int, remember_me: bool = False) -> str:
        """Create a new secure session."""
        session_id = secrets.token_urlsafe(32)
        
        # Store session data server-side
        self.sessions[session_id] = {
            'user_id': user_id,
            'created_at': datetime.now(),
            'expires_at': datetime.now() + (timedelta(days=30) if remember_me else timedelta(hours=1)),
            'ip_address': None,
            'user_agent': None
        }
        
        return session_id
    
    def validate_session(self, session_id: str) -> dict:
        """Validate and return session data."""
        session = self.sessions.get(session_id)
        
        if not session:
            return None
            
        # Check expiration
        if datetime.now() > session['expires_at']:
            del self.sessions[session_id]
            return None
            
        return session
    
    def destroy_session(self, session_id: str):
        """Logout - destroy session."""
        if session_id in self.sessions:
            del self.sessions[session_id]
    
    def regenerate_session(self, old_session_id: str) -> str:
        """Regenerate session ID to prevent session fixation."""
        old_session = self.validate_session(old_session_id)
        
        if not old_session:
            return None
        
        # Create new session
        new_session_id = self.create_session(
            old_session['user_id'],
            remember_me=(datetime.now() + timedelta(days=30) > old_session['expires_at'])
        )
        
        # Destroy old session
        self.destroy_session(old_session_id)
        
        return new_session_id

Secure Cookie Settings

# Example: Setting secure session cookie
from flask import Flask, make_response

app = Flask(__name__)
app.secret_key = secrets.token_hex(32)

@app.route('/login')
def login():
    response = make_response(redirect('/dashboard'))
    
    # Set session cookie with security flags
    response.set_cookie(
        'session_id',
        session_id,
        httponly=True,      # JavaScript cannot access
        secure=True,        # HTTPS only
        samesite='Strict', # CSRF protection
        max_age=3600,      # 1 hour
        path='/'           # Available on all paths
    )
    
    return response

🎫 JWT (JSON Web Tokens)

JWT is a compact, URL-safe token format for securely transmitting claims between parties.

JWT Structure

# JWT has three parts: Header.Payload.Signature
# Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

# 1. Header (Base64URL encoded)
{
  "alg": "HS256",  # Algorithm used for signature
  "typ": "JWT"
}

# 2. Payload (Base64URL encoded)
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622  # Expiration time
}

# 3. Signature
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret_key
)

Secure JWT Implementation

import jwt
from datetime import datetime, timedelta
import secrets

SECRET_KEY = secrets.token_hex(32)  # Keep secret!
ALGORITHM = 'HS256'
ACCESS_TOKEN_EXPIRE_MINUTES = 30

def create_access_token(user_id: int, role: str) -> str:
    """Create a JWT access token."""
    now = datetime.utcnow()
    expire = now + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    
    payload = {
        'sub': str(user_id),  # Subject (user ID)
        'role': role,         # User role
        'iat': now,          # Issued at
        'exp': expire        # Expiration
    }
    
    token = jwt.encode(payload, SECRET_KEY, algorithm=ALGORITHM)
    return token

def verify_token(token: str) -> dict:
    """Verify and decode JWT."""
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        return payload
    except jwt.ExpiredSignatureError:
        raise Exception("Token has expired")
    except jwt.InvalidTokenError:
        raise Exception("Invalid token")

# Example usage
token = create_access_token(user_id=123, role='admin')
print(f"Token: {token}")

# Verify
payload = verify_token(token)
print(f"User ID: {payload['sub']}")
print(f"Role: {payload['role']}")

⚠️ JWT Security Best Practices

  • Always set short expiration times
  • Store secret key securely (environment variables)
  • Validate all claims (issuer, audience, etc.)
  • Use RS256 (asymmetric) for production
  • Implement token refresh mechanism
  • Store tokens securely (httpOnly cookies)

👥 Role-Based Access Control (RBAC)

RBAC assigns permissions to roles rather than individual users.

RBAC Implementation

from enum import Enum
from functools import wraps

class Role(Enum):
    ADMIN = "admin"
    MODERATOR = "moderator"
    USER = "user"
    GUEST = "guest"

# Define permissions
PERMISSIONS = {
    Role.ADMIN: ['read', 'write', 'delete', 'manage_users', 'view_logs'],
    Role.MODERATOR: ['read', 'write', 'delete'],
    Role.USER: ['read', 'write'],
    Role.GUEST: ['read']
}

class User:
    def __init__(self, user_id: int, username: str, role: Role):
        self.user_id = user_id
        self.username = username
        self.role = role
    
    def has_permission(self, permission: str) -> bool:
        """Check if user has specific permission."""
        return permission in PERMISSIONS.get(self.role, [])
    
    def can_access(self, required_role: Role) -> bool:
        """Check if user's role meets minimum requirement."""
        role_hierarchy = {
            Role.GUEST: 0,
            Role.USER: 1,
            Role.MODERATOR: 2,
            Role.ADMIN: 3
        }
        return role_hierarchy[self.role] >= role_hierarchy[required_role]

def require_permission(permission: str):
    """Decorator to require specific permission."""
    def decorator(func):
        @wraps(func)
        def wrapper(user: User, *args, **kwargs):
            if not user.has_permission(permission):
                raise PermissionError(f"Permission denied: {permission}")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

# Example usage
admin = User(1, "admin_user", Role.ADMIN)
user = User(2, "regular_user", Role.USER)

print(f"Admin can delete: {admin.has_permission('delete')}")  # True
print(f"User can delete: {user.has_permission('delete')}")    # False

API Authorization Example

# Flask-RESTful with RBAC
from flask import Flask, jsonify, request
from functools import wraps

app = Flask(__name__)

def require_role(*allowed_roles):
    """Decorator to check user role."""
    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            # Get token from header
            auth_header = request.headers.get('Authorization')
            if not auth_header:
                return jsonify({'error': 'No token provided'}), 401
            
            # Decode token (simplified)
            token = auth_header.replace('Bearer ', '')
            user = verify_token(token)  # Your verification function
            
            if user['role'] not in allowed_roles:
                return jsonify({'error': 'Insufficient permissions'}), 403
            
            return f(user, *args, **kwargs)
        return decorated_function
    return decorator

# Usage
@app.route('/admin/users', methods=['GET'])
@require_role('admin')
def get_all_users(current_user):
    # Only admins can access this
    return jsonify({'users': [...]})

@app.route('/posts', methods=['POST'])
@require_role('user', 'moderator', 'admin')
def create_post(current_user):
    # User, moderator, or admin can create
    return jsonify({'message': 'Post created'})

🔐 Multi-Factor Authentication (MFA)

MFA requires multiple forms of verification, significantly improving security.

�something You Know

Password, PIN, security questions

�something You Have

Phone, security key, smart card

�something You Are

Fingerprint, face recognition, iris scan

TOTP (Time-based One-Time Password)

import pyotp
import qrcode
from io import BytesIO
import base64

def generate_secret():
    """Generate a new TOTP secret."""
    return pyotp.random_base32()

def get_provisioning_uri(secret: str, email: str, issuer: str = "MyApp") -> str:
    """Get URI for QR code generation."""
    totp = pyotp.TOTP(secret)
    return totp.provisioning_uri(name=email, issuer_name=issuer)

def verify_code(secret: str, code: str) -> bool:
    """Verify TOTP code."""
    totp = pyotp.TOTP(secret)
    return totp.verify(code)

# Example usage
secret = generate_secret()
print(f"Secret: {secret}")

# Get QR code for Google Authenticator
uri = get_provisioning_uri(secret, "user@example.com", "CyberGuard")
print(f"URI: {uri}")

# Verify a code
code = input("Enter code from authenticator: ")
is_valid = verify_code(secret, code)
print(f"Valid: {is_valid}")

🎯 Mini Project: Secure Authentication System

Build a complete authentication system:

"""
Secure Authentication System Requirements:
1. User registration with password hashing (bcrypt)
2. Login with password verification
3. JWT token generation
4. Protected routes
5. Role-based access control
"""

# Key Components:
# - User model with hashed passwords
# - Registration endpoint
# - Login endpoint with JWT
# - Decorators for protected routes
# - Role-based middleware

Requirements:

  • Secure password hashing with bcrypt
  • JWT token generation and validation
  • Role-based access control
  • Session management
  • Login/Logout functionality

🏋️ Practice Challenge

Challenge: Implement Complete Auth System

Build a complete authentication system with:

  1. User registration with email verification
  2. Secure login with rate limiting
  3. JWT-based session management
  4. MFA using TOTP
  5. Role-based permissions
  6. Password reset functionality

📝 Summary

Key Takeaways:

  • Authentication verifies identity; Authorization controls access
  • Never store plain text passwords - always use bcrypt or Argon2
  • Sessions maintain state; use secure cookie settings
  • JWT provides stateless authentication; secure implementation is critical
  • RBAC assigns permissions to roles, simplifying access management
  • MFA adds additional security layer

Vocabulary:

  • AuthN - Authentication, verifying identity
  • AuthZ - Authorization, controlling access
  • JWT - JSON Web Token for stateless auth
  • RBAC - Role-Based Access Control
  • MFA - Multi-Factor Authentication
  • TOTP - Time-based One-Time Password