Files
I-SecretUpdate/auth/graph_authenticator.py
T
2025-12-19 12:58:58 +01:00

106 lines
3.3 KiB
Python

"""
Microsoft Graph Authentication Module
Handles authentication to Microsoft Graph API for app registration management.
"""
from azure.identity import InteractiveBrowserCredential
from msgraph import GraphServiceClient
from typing import Optional
import config
class GraphAuthenticator:
"""Handles Microsoft Graph authentication and client creation."""
def __init__(self, client_id: str = None):
"""
Initialize the Graph authenticator.
Args:
client_id: Application Client ID (defaults to config.CLIENT_ID)
"""
self.client_id = client_id or config.CLIENT_ID
self.credential: Optional[InteractiveBrowserCredential] = None
self.client: Optional[GraphServiceClient] = None
async def authenticate(self) -> bool:
"""
Authenticate to Microsoft Graph using interactive browser login.
Returns:
bool: True if authentication succeeded, False otherwise
Raises:
Exception: If authentication fails
"""
try:
# Create interactive browser credential
# Using "organizations" allows login with any organizational account
# additionally_allowed_tenants="*" allows acquiring tokens for any tenant (needed for Key Vault access)
self.credential = InteractiveBrowserCredential(
tenant_id="organizations",
client_id=self.client_id,
additionally_allowed_tenants=["*"]
)
# Define scopes for Microsoft Graph
scopes = ['https://graph.microsoft.com/.default']
# Create Graph service client
self.client = GraphServiceClient(
credentials=self.credential,
scopes=scopes
)
# Authenticate to Graph API FIRST
# This triggers the initial browser auth for Graph scope
# Then Management API will use SSO (single sign-on) from this auth
me = await self.client.me.get()
if me:
print(f"Successfully authenticated as: {me.display_name} ({me.user_principal_name})")
return True
return False
except Exception as e:
raise Exception(f"Graph authentication failed: {str(e)}")
def get_client(self) -> GraphServiceClient:
"""
Get the authenticated Graph service client.
Returns:
GraphServiceClient: The authenticated client
Raises:
Exception: If not authenticated
"""
if not self.client:
raise Exception("Not authenticated. Call authenticate() first.")
return self.client
def get_credential(self) -> InteractiveBrowserCredential:
"""
Get the credential object for reuse in other Azure services.
Returns:
InteractiveBrowserCredential: The credential object
Raises:
Exception: If not authenticated
"""
if not self.credential:
raise Exception("Not authenticated. Call authenticate() first.")
return self.credential
def is_authenticated(self) -> bool:
"""
Check if currently authenticated.
Returns:
bool: True if authenticated, False otherwise
"""
return self.client is not None and self.credential is not None