Authentication and Authorization Testing

Authentication and Authorization Testing

Authentication testing verifies that only properly authenticated users can access protected resources. Test cases should cover various authentication failures, token handling edge cases, and session management vulnerabilities. Authorization testing ensures authenticated users can only access resources they're permitted to use.

# Python pytest framework for API security testing
import pytest
import requests
import jwt
import time
from datetime import datetime, timedelta
import json

class TestAPIAuthentication:
    
    @pytest.fixture
    def api_base_url(self):
        return "https://api.example.com"
    
    @pytest.fixture
    def valid_token(self):
        # Generate valid test token
        payload = {
            'sub': 'test_user',
            'exp': datetime.utcnow() + timedelta(hours=1),
            'scope': 'read write'
        }
        return jwt.encode(payload, 'test_secret', algorithm='HS256')
    
    def test_missing_authentication(self, api_base_url):
        """Test that protected endpoints reject requests without authentication"""
        response = requests.get(f"{api_base_url}/api/protected/resource")
        assert response.status_code == 401
        assert 'error' in response.json()
        assert response.json()['error'] == 'authentication_required'
    
    def test_invalid_token_format(self, api_base_url):
        """Test various invalid token formats"""
        invalid_tokens = [
            'invalid_token',
            'Bearer',
            'Bearer ',
            'Basic dGVzdDp0ZXN0',  # Wrong auth scheme
            'Bearer ' + 'a' * 1000,  # Extremely long token
            'Bearer null',
            'Bearer undefined',
            'Bearer {"sub":"test"}'  # JSON instead of JWT
        ]
        
        for token in invalid_tokens:
            response = requests.get(
                f"{api_base_url}/api/protected/resource",
                headers={'Authorization': token}
            )
            assert response.status_code == 401, f"Failed for token: {token}"
    
    def test_expired_token(self, api_base_url):
        """Test that expired tokens are rejected"""
        expired_payload = {
            'sub': 'test_user',
            'exp': datetime.utcnow() - timedelta(hours=1),  # Expired 1 hour ago
            'scope': 'read write'
        }
        expired_token = jwt.encode(expired_payload, 'test_secret', algorithm='HS256')
        
        response = requests.get(
            f"{api_base_url}/api/protected/resource",
            headers={'Authorization': f'Bearer {expired_token}'}
        )
        assert response.status_code == 401
        assert response.json()['error'] == 'token_expired'
    
    def test_token_signature_tampering(self, api_base_url):
        """Test that tampered tokens are rejected"""
        # Create valid token
        payload = {
            'sub': 'test_user',
            'exp': datetime.utcnow() + timedelta(hours=1),
            'scope': 'read write'
        }
        valid_token = jwt.encode(payload, 'test_secret', algorithm='HS256')
        
        # Tamper with signature
        parts = valid_token.split('.')
        tampered_token = f"{parts[0]}.{parts[1]}.tampered_signature"
        
        response = requests.get(
            f"{api_base_url}/api/protected/resource",
            headers={'Authorization': f'Bearer {tampered_token}'}
        )
        assert response.status_code == 401
        assert response.json()['error'] == 'invalid_token'
    
    def test_algorithm_confusion_attack(self, api_base_url):
        """Test protection against algorithm confusion attacks"""
        # Try to use 'none' algorithm
        header = {"alg": "none", "typ": "JWT"}
        payload = {"sub": "attacker", "admin": True}
        
        token = f"{base64url_encode(json.dumps(header))}.{base64url_encode(json.dumps(payload))}."
        
        response = requests.get(
            f"{api_base_url}/api/protected/resource",
            headers={'Authorization': f'Bearer {token}'}
        )
        assert response.status_code == 401
    
    def test_token_replay_attack(self, api_base_url, valid_token):
        """Test protection against token replay attacks"""
        # Use token successfully
        response1 = requests.post(
            f"{api_base_url}/api/sensitive/action",
            headers={'Authorization': f'Bearer {valid_token}'},
            json={'action': 'transfer', 'amount': 1000}
        )
        assert response1.status_code == 200
        request_id = response1.json().get('request_id')
        
        # Try to replay the same request
        response2 = requests.post(
            f"{api_base_url}/api/sensitive/action",
            headers={
                'Authorization': f'Bearer {valid_token}',
                'X-Request-ID': request_id  # Same request ID
            },
            json={'action': 'transfer', 'amount': 1000}
        )
        assert response2.status_code == 409  # Conflict - duplicate request

class TestAPIAuthorization:
    
    @pytest.fixture
    def user_token(self):
        """Token for regular user"""
        payload = {
            'sub': 'user123',
            'role': 'user',
            'scope': 'read:own write:own'
        }
        return jwt.encode(payload, 'test_secret', algorithm='HS256')
    
    @pytest.fixture
    def admin_token(self):
        """Token for admin user"""
        payload = {
            'sub': 'admin123',
            'role': 'admin',
            'scope': 'read:all write:all admin:all'
        }
        return jwt.encode(payload, 'test_secret', algorithm='HS256')
    
    def test_horizontal_privilege_escalation(self, api_base_url, user_token):
        """Test that users cannot access other users' resources"""
        # Try to access another user's data
        response = requests.get(
            f"{api_base_url}/api/users/user456/profile",
            headers={'Authorization': f'Bearer {user_token}'}
        )
        assert response.status_code == 403
        assert response.json()['error'] == 'insufficient_permissions'
    
    def test_vertical_privilege_escalation(self, api_base_url, user_token):
        """Test that regular users cannot perform admin actions"""
        admin_actions = [
            ('DELETE', '/api/users/user456'),
            ('POST', '/api/admin/settings'),
            ('PUT', '/api/users/user456/role'),
            ('GET', '/api/admin/logs')
        ]
        
        for method, endpoint in admin_actions:
            response = requests.request(
                method,
                f"{api_base_url}{endpoint}",
                headers={'Authorization': f'Bearer {user_token}'}
            )
            assert response.status_code == 403, f"Failed for {method} {endpoint}"
    
    def test_scope_enforcement(self, api_base_url):
        """Test that token scopes are properly enforced"""
        # Token with only read scope
        read_only_token = jwt.encode({
            'sub': 'user123',
            'scope': 'read:own'
        }, 'test_secret', algorithm='HS256')
        
        # Try write operation with read-only token
        response = requests.put(
            f"{api_base_url}/api/users/user123/profile",
            headers={'Authorization': f'Bearer {read_only_token}'},
            json={'name': 'New Name'}
        )
        assert response.status_code == 403
        assert 'insufficient_scope' in response.json()['error']
    
    def test_parameter_manipulation(self, api_base_url, user_token):
        """Test authorization with manipulated parameters"""
        # Try to elevate privileges through parameter manipulation
        manipulated_requests = [
            {'role': 'admin'},
            {'isAdmin': True},
            {'permissions': ['admin']},
            {'scope': 'admin:all'}
        ]
        
        for payload in manipulated_requests:
            response = requests.put(
                f"{api_base_url}/api/users/user123/profile",
                headers={'Authorization': f'Bearer {user_token}'},
                json=payload
            )
            
            # Verify role hasn't changed
            profile = requests.get(
                f"{api_base_url}/api/users/user123/profile",
                headers={'Authorization': f'Bearer {user_token}'}
            ).json()
            
            assert profile.get('role') != 'admin'
            assert not profile.get('isAdmin')