""" Authentication module for Microsoft Graph API. """ import os import msal def get_access_token(scopes): """ Authenticate with Microsoft Graph API and obtain an access token. Args: scopes (list): List of scopes to request. Returns: tuple: (access_token, headers) where access_token is the token string and headers is a dict with Authorization header. Raises: ValueError: If environment variables are missing. Exception: If authentication fails. """ # Read Azure app credentials from environment variables client_id = os.getenv('AZURE_CLIENT_ID') tenant_id = os.getenv('AZURE_TENANT_ID') if not client_id or not tenant_id: raise ValueError("Please set the AZURE_CLIENT_ID and AZURE_TENANT_ID environment variables.") # Token cache cache = msal.SerializableTokenCache() cache_file = 'token_cache.bin' if os.path.exists(cache_file): cache.deserialize(open(cache_file, 'r').read()) # Authentication authority = f'https://login.microsoftonline.com/{tenant_id}' app = msal.PublicClientApplication(client_id, authority=authority, token_cache=cache) accounts = app.get_accounts() if accounts: token_response = app.acquire_token_silent(scopes, account=accounts[0]) else: flow = app.initiate_device_flow(scopes=scopes) if 'user_code' not in flow: raise Exception("Failed to create device flow") from rich import print from rich.panel import Panel print(Panel(flow['message'], border_style="magenta", padding=2, title="MSAL Login Flow Link")) token_response = app.acquire_token_by_device_flow(flow) if 'access_token' not in token_response: raise Exception("Failed to acquire token") # Save token cache with open(cache_file, 'w') as f: f.write(cache.serialize()) access_token = token_response['access_token'] headers = {'Authorization': f'Bearer {access_token}', 'Prefer': 'outlook.body-content-type="text",IdType="ImmutableId"'} return access_token, headers