This commit is contained in:
2025-12-22 09:57:49 +01:00
parent 87865e2c6d
commit e00c47a56f
4 changed files with 236 additions and 78 deletions
+90 -30
View File
@@ -21,12 +21,57 @@ class AzureAuthenticator:
self.credential: Optional[InteractiveBrowserCredential] = None
self.subscriptions: List[Dict[str, str]] = []
def authenticate(self, credential: InteractiveBrowserCredential = None) -> bool:
def discover_tenant_id(self) -> tuple[str, InteractiveBrowserCredential, List[Dict[str, str]]]:
"""
Authenticate to Azure. Can reuse credential from Graph authenticator.
Discover user's tenant ID by authenticating with organizations endpoint.
Returns:
tuple: (tenant_id, credential, subscriptions) - The discovered tenant ID, credential to reuse, and subscription list
Raises:
Exception: If no subscriptions found or authentication fails
"""
try:
# Create temporary credential with "organizations" for discovery
temp_credential = InteractiveBrowserCredential(
tenant_id="organizations",
additionally_allowed_tenants=["*"]
)
# List subscriptions to extract tenant
sub_client = SubscriptionClient(temp_credential)
subscriptions_list = list(sub_client.subscriptions.list())
if not subscriptions_list:
raise Exception("No Azure subscriptions found. Please ensure you have access to at least one subscription.")
# Extract tenant ID from first subscription
tenant_id = subscriptions_list[0].tenant_id
print(f"Discovered Tenant ID: {tenant_id}")
# Convert subscriptions to dict format
subscriptions = [
{
'id': sub.subscription_id,
'name': sub.display_name
}
for sub in subscriptions_list
]
# Return tenant_id, credential, and subscriptions for reuse
return tenant_id, temp_credential, subscriptions
except Exception as e:
raise Exception(f"Failed to discover tenant: {str(e)}")
def authenticate(self, credential: InteractiveBrowserCredential = None, tenant_id: str = None, subscriptions: List[Dict[str, str]] = None) -> bool:
"""
Authenticate to Azure. Can reuse credential, use specific tenant, or discover tenant.
Args:
credential: Optional credential to reuse (from GraphAuthenticator)
tenant_id: Optional specific tenant ID to use
subscriptions: Optional pre-fetched subscriptions list (avoids re-listing)
Returns:
bool: True if authentication succeeded, False otherwise
@@ -36,45 +81,60 @@ class AzureAuthenticator:
"""
try:
if credential:
# Reuse credential from Graph authentication
# Reuse credential (recommended path to avoid multiple auth prompts)
self.credential = credential
else:
# Create new interactive browser credential with "organizations" tenant
# This allows the user to login with any organizational account
# additionally_allowed_tenants="*" allows acquiring tokens for any tenant
# Create credential with specific tenant ID
if not tenant_id:
raise Exception("Either credential or tenant_id must be provided")
self.tenant_id = tenant_id
# Create NEW credential with specific tenant_id (avoids redirect)
self.credential = InteractiveBrowserCredential(
tenant_id="organizations",
tenant_id=tenant_id,
additionally_allowed_tenants=["*"]
)
# List all subscriptions (this will use the cached token from Graph auth)
sub_client = SubscriptionClient(self.credential)
subscriptions_list = list(sub_client.subscriptions.list())
# Use provided subscriptions or list them if not provided
if subscriptions:
# Reuse pre-fetched subscriptions (avoids duplicate API call)
self.subscriptions = subscriptions
# Extract tenant ID from the first subscription
if subscriptions_list:
# Get tenant ID from subscription (format: /subscriptions/{sub-id})
# The tenant info is in the subscription object
first_sub = subscriptions_list[0]
self.tenant_id = first_sub.tenant_id if hasattr(first_sub, 'tenant_id') else None
if self.tenant_id:
print(f"Detected Tenant ID: {self.tenant_id}")
# Set tenant ID if provided
if tenant_id:
self.tenant_id = tenant_id
if subscriptions_list:
print(f"Successfully authenticated to Azure. Found {len(subscriptions_list)} subscription(s).")
# Store subscriptions for later selection
self.subscriptions = [
{
'id': sub.subscription_id,
'name': sub.display_name
}
for sub in subscriptions_list
]
print(f"Using cached authentication. Found {len(subscriptions)} subscription(s).")
return True
else:
# List all subscriptions (fallback for legacy code path)
sub_client = SubscriptionClient(self.credential)
subscriptions_list = list(sub_client.subscriptions.list())
return False
# Extract tenant ID if not already set
if subscriptions_list and not self.tenant_id:
first_sub = subscriptions_list[0]
self.tenant_id = first_sub.tenant_id if hasattr(first_sub, 'tenant_id') else None
if self.tenant_id:
print(f"Detected Tenant ID: {self.tenant_id}")
if subscriptions_list:
print(f"Successfully authenticated to Azure. Found {len(subscriptions_list)} subscription(s).")
# Store subscriptions for later selection
self.subscriptions = [
{
'id': sub.subscription_id,
'name': sub.display_name
}
for sub in subscriptions_list
]
return True
return False
except Exception as e:
raise Exception(f"Azure authentication failed: {str(e)}")