CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-adal

Azure Active Directory Authentication Library for Python that provides OAuth2/OpenID Connect authentication flows and token management for accessing Azure AD protected resources

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

logging-error-handling.mddocs/

Logging and Error Handling

Comprehensive logging system with PII scrubbing capabilities and custom exception handling for authentication errors. ADAL provides detailed logging for debugging authentication issues while protecting sensitive information.

Capabilities

Logging Configuration

Configure the ADAL logger with custom log levels and handlers. The logging system supports correlation IDs for request tracking and PII scrubbing for security.

def set_logging_options(options=None):
    """
    Configure ADAL logger settings.
    
    Parameters:
    - options (dict, optional): Logging configuration with keys:
        - 'level' (str): Log level ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
        - 'handler' (logging.Handler): Custom log handler instance
    
    If options is None, resets to default logging configuration.
    """

Usage Examples:

import adal
import logging

# Basic logging to console
adal.set_logging_options({
    'level': 'DEBUG'
})

# Log to file with custom handler
adal.set_logging_options({
    'level': 'INFO',
    'handler': logging.FileHandler('adal.log')
})

# Custom formatting
handler = logging.StreamHandler()
formatter = logging.Formatter(
    '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)

adal.set_logging_options({
    'level': 'WARNING',
    'handler': handler
})

# Reset to defaults
adal.set_logging_options()

Get Current Logging Configuration

Retrieve the current logging configuration for inspection or backup.

def get_logging_options():
    """
    Get current logging configuration.
    
    Returns:
    dict: Current logging options with 'level' key
    """

Usage Example:

# Save current configuration
current_config = adal.get_logging_options()
print(f"Current log level: {current_config['level']}")

# Temporarily change logging
adal.set_logging_options({'level': 'DEBUG'})

# Do some debugging work
context = adal.AuthenticationContext('https://login.microsoftonline.com/tenant-id')
# ... authentication calls with detailed logging

# Restore original configuration
adal.set_logging_options(current_config)

ADAL Logger Name Constant

The standard logger name used by ADAL for all logging operations.

ADAL_LOGGER_NAME: str  # Value: "adal-python"

Usage Example:

import logging
import adal

# Get the ADAL logger directly
adal_logger = logging.getLogger(adal.ADAL_LOGGER_NAME)

# Add custom handler to ADAL logger
custom_handler = logging.FileHandler('custom_adal.log')
custom_handler.setLevel(logging.INFO)
adal_logger.addHandler(custom_handler)

# Configure formatter
formatter = logging.Formatter(
    '%(asctime)s [%(correlation_id)s] %(levelname)s: %(message)s'
)
custom_handler.setFormatter(formatter)

Error Handling

AdalError Exception

Custom exception class for ADAL-specific authentication and authorization errors. Provides detailed error information and optional response data.

class AdalError(Exception):
    def __init__(self, error_msg, error_response=None):
        """
        ADAL-specific exception for authentication errors.
        
        Parameters:
        - error_msg (str): Human-readable error message
        - error_response (dict, optional): Detailed error response from Azure AD
        
        Attributes:
        - error_response (dict): Azure AD error response details (if available)
        """

Usage Examples:

import adal

context = adal.AuthenticationContext('https://login.microsoftonline.com/tenant-id')

try:
    token = context.acquire_token_with_client_credentials(
        resource='https://management.azure.com/',
        client_id='invalid-client-id',
        client_secret='invalid-secret'
    )
except adal.AdalError as e:
    print(f"Authentication failed: {e}")
    
    # Check for detailed error response
    if e.error_response:
        print(f"Error code: {e.error_response.get('error')}")
        print(f"Error description: {e.error_response.get('error_description')}")
        print(f"Correlation ID: {e.error_response.get('correlation_id')}")
        print(f"Timestamp: {e.error_response.get('timestamp')}")

Common Error Scenarios

Handle different types of authentication errors appropriately:

import adal

def robust_authentication():
    context = adal.AuthenticationContext('https://login.microsoftonline.com/tenant-id')
    
    try:
        token = context.acquire_token_with_username_password(
            resource='https://management.azure.com/',
            username='user@tenant.com',
            password='user-password',
            client_id='your-client-id'
        )
        return token
        
    except adal.AdalError as e:
        error_msg = str(e).lower()
        
        if 'invalid_client' in error_msg:
            print("Error: Invalid client ID or client not configured properly")
            
        elif 'invalid_grant' in error_msg:
            print("Error: Invalid username/password or account disabled")
            
        elif 'unauthorized_client' in error_msg:
            print("Error: Client not authorized for this authentication flow")
            
        elif 'invalid_resource' in error_msg:
            print("Error: Invalid resource URI")
            
        elif 'authority_not_found' in error_msg:
            print("Error: Invalid tenant ID or authority URL")
            
        elif 'network' in error_msg or 'timeout' in error_msg:
            print("Error: Network connectivity issue")
            
        else:
            print(f"Unexpected authentication error: {e}")
            
        # Log detailed error for debugging
        if e.error_response:
            print("Detailed error response:")
            for key, value in e.error_response.items():
                print(f"  {key}: {value}")
        
        return None

Complete Logging Example

import adal
import logging
import sys
from datetime import datetime

class AdalLoggingSetup:
    def __init__(self, log_level='INFO', log_to_file=False, filename=None):
        self.log_level = log_level
        self.log_to_file = log_to_file
        self.filename = filename or f'adal_{datetime.now().strftime("%Y%m%d_%H%M%S")}.log'
        self.setup_logging()
    
    def setup_logging(self):
        """Configure comprehensive ADAL logging"""
        
        # Create custom handler
        if self.log_to_file:
            handler = logging.FileHandler(self.filename)
        else:
            handler = logging.StreamHandler(sys.stdout)
        
        # Set log level
        handler.setLevel(getattr(logging, self.log_level.upper()))
        
        # Create detailed formatter
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(funcName)s:%(lineno)d - %(message)s'
        )
        handler.setFormatter(formatter)
        
        # Configure ADAL logging
        adal.set_logging_options({
            'level': self.log_level,
            'handler': handler
        })
        
        print(f"ADAL logging configured: level={self.log_level}, file={self.filename if self.log_to_file else 'console'}")

def demo_with_logging():
    # Setup comprehensive logging
    logging_setup = AdalLoggingSetup(
        log_level='DEBUG',
        log_to_file=True,
        filename='adal_debug.log'
    )
    
    # Create authentication context
    authority = 'https://login.microsoftonline.com/your-tenant-id'
    context = adal.AuthenticationContext(authority)
    
    try:
        # This will generate detailed debug logs
        print("Attempting authentication...")
        token = context.acquire_token_with_client_credentials(
            resource='https://management.azure.com/',
            client_id='your-client-id',
            client_secret='your-client-secret'
        )
        
        print("Authentication successful!")
        print(f"Token type: {token.get('tokenType')}")
        print(f"Expires on: {token.get('expiresOn')}")
        
    except adal.AdalError as e:
        print(f"Authentication failed: {e}")
        
        # Log error details
        adal_logger = logging.getLogger(adal.ADAL_LOGGER_NAME)
        adal_logger.error(f"Authentication error: {e}")
        
        if e.error_response:
            adal_logger.error(f"Error response: {e.error_response}")
    
    # Show current logging configuration
    config = adal.get_logging_options()
    print(f"Final logging configuration: {config}")

if __name__ == '__main__':
    demo_with_logging()

PII Protection

ADAL automatically scrubs personally identifiable information (PII) from logs when PII logging is disabled (default). When creating an AuthenticationContext, you can control PII logging:

# PII scrubbing enabled (default)
context = adal.AuthenticationContext(
    'https://login.microsoftonline.com/tenant-id',
    enable_pii=False
)

# PII logging enabled (for debugging only)
context = adal.AuthenticationContext(
    'https://login.microsoftonline.com/tenant-id',
    enable_pii=True
)

Important: Only enable PII logging in secure development environments. Never enable PII logging in production systems.

Best Practices

  1. Use appropriate log levels: DEBUG for development, INFO for production monitoring
  2. Secure log files: Ensure log files have appropriate permissions and are stored securely
  3. Rotate logs: Implement log rotation to prevent disk space issues
  4. Monitor errors: Set up alerts for authentication failures in production
  5. Correlation IDs: Use correlation IDs to track requests across systems
  6. PII protection: Keep PII scrubbing enabled in production environments

Install with Tessl CLI

npx tessl i tessl/pypi-adal

docs

authentication-flows.md

device-code-flow.md

index.md

logging-error-handling.md

token-caching.md

tile.json