class AzureSSO
A class that handles Azure Active Directory (Azure AD) Single Sign-On (SSO) authentication using OAuth 2.0 authorization code flow.
/tf/active/vicechatdev/docchat/auth/azure_auth.py
18 - 119
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 IDtenant_id: Azure AD tenant IDclient_secret: Azure AD application client secretredirect_uri: Callback URL after authenticationscope: 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
msaluuidurllib.parselogging
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
Tags
Similar Components
AI-powered semantic similarity - components with related functionality:
-
class AzureSSO_v2 98.4% similar
-
class AzureSSO_v1 97.3% similar
-
class SSOCallbackHandler 73.1% similar
-
function auth_callback 71.1% similar
-
class AuthCodeHandler 68.4% similar