🔍 Code Extractor

class AzureSSO

Maturity: 51

A class that handles Azure Active Directory (Azure AD) Single Sign-On (SSO) authentication using OAuth 2.0 authorization code flow.

File:
/tf/active/vicechatdev/docchat/auth/azure_auth.py
Lines:
18 - 119
Complexity:
moderate

Purpose

This class provides a complete implementation for Azure AD SSO authentication. It manages the OAuth 2.0 authorization code flow by generating authorization URLs for user login, exchanging authorization codes for access tokens, and handling the necessary Azure AD endpoints. It uses the MSAL (Microsoft Authentication Library) for secure token acquisition and manages scope filtering to comply with Azure AD requirements.

Source Code

class AzureSSO:
    """Class to handle Azure Active Directory SSO authentication."""
    
    def __init__(self, client_id, tenant_id, client_secret, redirect_uri, scope):
        """
        Initialize Azure SSO with the required configuration.
        
        Args:
            client_id: Azure AD application client ID
            tenant_id: Azure AD tenant ID
            client_secret: Azure AD application client secret
            redirect_uri: Callback URL after authentication
            scope: OAuth scopes to request
        """
        self.client_id = client_id
        self.tenant_id = tenant_id
        self.client_secret = client_secret
        self.redirect_uri = redirect_uri
        self.scope = scope
        self.authority = f"https://login.microsoftonline.com/{tenant_id}"
        self.authorize_endpoint = f"{self.authority}/oauth2/v2.0/authorize"
        self.token_endpoint = f"{self.authority}/oauth2/v2.0/token"
    
    def get_auth_url(self, response_mode='query'):
        """
        Generate the authorization URL for redirecting users to Azure login.
        
        Parameters:
            response_mode (str): How Azure should return the response ('query' or 'fragment')
            
        Returns:
            str: The authorization URL
        """
        # Create URL parameters
        params = {
            'client_id': self.client_id,
            'response_type': 'code',
            'redirect_uri': self.redirect_uri,
            'scope': self.scope,
            'response_mode': response_mode,
            'state': str(uuid.uuid4()),  # CSRF protection
        }
        
        # Use urllib.parse to properly encode parameters
        auth_url = f"{self.authorize_endpoint}?{urllib.parse.urlencode(params)}"
        
        return auth_url
        
    def get_token_from_code(self, auth_code: str) -> dict:
        """
        Exchange authorization code for access and ID tokens.
        
        Args:
            auth_code: Authorization code received from Azure AD
            
        Returns:
            dict: Token response containing access_token, id_token, etc.
        """
        try:
            logger.info(f"Creating MSAL application with client_id: {self.client_id[:8]}...")
            logger.info(f"Authority: {self.authority}")
            logger.info(f"Redirect URI for token exchange: {self.redirect_uri}")
            
            # Use MSAL library to handle token exchange
            app = msal.ConfidentialClientApplication(
                self.client_id,
                authority=self.authority,
                client_credential=self.client_secret
            )
            
            # Filter out reserved scopes for token exchange
            reserved_scopes = {'profile', 'openid', 'offline_access'}
            requested_scopes = self.scope.split()
            
            # Remove reserved scopes for token exchange
            filtered_scopes = [scope for scope in requested_scopes if scope not in reserved_scopes]
            
            # If all scopes were reserved, use at least one non-reserved scope
            if not filtered_scopes:
                filtered_scopes = ['User.Read']
                
            logger.info(f"Original scopes: {requested_scopes}")
            logger.info(f"Filtered scopes for token exchange: {filtered_scopes}")
            
            # Get token using authorization code with filtered scopes
            result = app.acquire_token_by_authorization_code(
                auth_code,
                scopes=filtered_scopes,
                redirect_uri=self.redirect_uri
            )
            
            if "error" in result:
                logger.error(f"Error acquiring token: {result.get('error')}")
                logger.error(f"Error description: {result.get('error_description')}")
                return {}
                
            logger.info(f"Token acquired successfully")
            return result
            
        except Exception as e:
            logger.error(f"Exception in get_token_from_code: {e}", exc_info=True)
            return {}

Parameters

Name Type Default Kind
bases - -

Parameter Details

client_id: The Azure AD application (client) ID obtained from Azure portal when registering your application. This uniquely identifies your application to Azure AD.

tenant_id: The Azure AD tenant ID (directory ID) that identifies your organization's Azure AD instance. Can be a GUID or domain name.

client_secret: The client secret (application password) generated for your Azure AD application. Used to authenticate your application when exchanging authorization codes for tokens.

redirect_uri: The callback URL where Azure AD will redirect users after authentication. Must match exactly with the redirect URI configured in your Azure AD application registration.

scope: Space-separated string of OAuth scopes to request (e.g., 'User.Read openid profile'). Defines what permissions your application is requesting from the user.

Return Value

Instantiation returns an AzureSSO object configured with the provided credentials and endpoints. The get_auth_url() method returns a string URL for user authentication. The get_token_from_code() method returns a dictionary containing token information (access_token, id_token, token_type, expires_in, etc.) on success, or an empty dictionary on failure.

Class Interface

Methods

__init__(self, client_id, tenant_id, client_secret, redirect_uri, scope)

Purpose: Initialize the AzureSSO instance with Azure AD configuration and construct the necessary OAuth endpoints

Parameters:

  • client_id: Azure AD application client ID
  • tenant_id: Azure AD tenant ID
  • client_secret: Azure AD application client secret
  • redirect_uri: Callback URL after authentication
  • scope: OAuth scopes to request

Returns: None - initializes instance attributes

get_auth_url(self, response_mode='query') -> str

Purpose: Generate the authorization URL to redirect users to Azure AD login page

Parameters:

  • response_mode: How Azure should return the response - 'query' (default) appends parameters to URL, 'fragment' uses URL fragment

Returns: String containing the complete authorization URL with encoded parameters including client_id, redirect_uri, scope, and a randomly generated state for CSRF protection

get_token_from_code(self, auth_code: str) -> dict

Purpose: Exchange the authorization code received from Azure AD for access and ID tokens using MSAL library

Parameters:

  • auth_code: The authorization code received from Azure AD after user authentication

Returns: Dictionary containing token information (access_token, id_token, token_type, expires_in, scope, etc.) on success, or empty dictionary on failure. Check for 'error' key to detect failures.

Attributes

Name Type Description Scope
client_id str Azure AD application client ID instance
tenant_id str Azure AD tenant ID instance
client_secret str Azure AD application client secret for authentication instance
redirect_uri str Callback URL where Azure AD redirects after authentication instance
scope str Space-separated string of OAuth scopes to request instance
authority str The Azure AD authority URL constructed from tenant_id (e.g., https://login.microsoftonline.com/{tenant_id}) instance
authorize_endpoint str The OAuth 2.0 authorization endpoint URL for initiating user login instance
token_endpoint str The OAuth 2.0 token endpoint URL for exchanging authorization codes for tokens instance

Dependencies

  • msal
  • uuid
  • urllib.parse
  • logging

Required Imports

import msal
import uuid
import urllib.parse
import logging

Usage Example

import msal
import uuid
import urllib.parse
import logging

# Setup logger
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

# Initialize AzureSSO
azure_sso = AzureSSO(
    client_id='your-client-id',
    tenant_id='your-tenant-id',
    client_secret='your-client-secret',
    redirect_uri='https://yourapp.com/callback',
    scope='User.Read openid profile'
)

# Step 1: Generate authorization URL and redirect user
auth_url = azure_sso.get_auth_url(response_mode='query')
print(f'Redirect user to: {auth_url}')

# Step 2: After user authenticates, Azure redirects back with auth code
# Extract the code from the callback URL parameters
auth_code = 'code-received-from-azure'

# Step 3: Exchange authorization code for tokens
token_response = azure_sso.get_token_from_code(auth_code)

if token_response:
    access_token = token_response.get('access_token')
    id_token = token_response.get('id_token')
    print(f'Access token obtained: {access_token[:20]}...')
else:
    print('Failed to obtain tokens')

Best Practices

  • Always store client_secret securely (environment variables, key vault) and never commit it to version control
  • The state parameter in get_auth_url() provides CSRF protection - store it in session and validate it when receiving the callback
  • Handle token expiration by checking the 'expires_in' field in the token response and implementing token refresh logic
  • The redirect_uri must match exactly (including trailing slashes) with what's configured in Azure AD
  • Reserved scopes (profile, openid, offline_access) are automatically filtered during token exchange as they're handled implicitly by Azure AD
  • Always check if the token_response dictionary contains an 'error' key before using the tokens
  • Use HTTPS for redirect_uri in production environments
  • The class is stateless except for configuration - safe to reuse the same instance for multiple authentication flows
  • Implement proper error handling around get_token_from_code() as network issues or invalid codes will return empty dictionaries
  • Log messages use truncated client_id for security - avoid logging full credentials

Similar Components

AI-powered semantic similarity - components with related functionality:

  • class AzureSSO_v2 98.4% similar

    A class that handles Azure Active Directory (Azure AD) Single Sign-On (SSO) authentication using OAuth 2.0 authorization code flow.

    From: /tf/active/vicechatdev/CDocs/auth/azure_auth.py
  • class AzureSSO_v1 97.3% similar

    A class that handles Azure Active Directory (Azure AD) Single Sign-On (SSO) authentication using OAuth 2.0 authorization code flow.

    From: /tf/active/vicechatdev/vice_ai/auth/azure_auth.py
  • class SSOCallbackHandler 73.1% similar

    A Tornado RequestHandler that processes OAuth 2.0 callbacks from Azure AD, exchanges authorization codes for access tokens, validates user identity, and sets authentication cookies for SSO integration.

    From: /tf/active/vicechatdev/CDocs/sso_plugin.py
  • function auth_callback 71.1% similar

    OAuth callback handler that processes Azure SSO authentication responses, exchanges authorization codes for access tokens, and establishes user sessions.

    From: /tf/active/vicechatdev/vice_ai/complex_app.py
  • class AuthCodeHandler 68.4% similar

    A callable handler class that processes OAuth authentication codes from form POST requests, exchanges them for access tokens, and authenticates users via Azure SSO.

    From: /tf/active/vicechatdev/CDocs/main.py
← Back to Browse