diff --git a/src/ContentProcessor/src/libs/utils/azure_credential_utils.py b/src/ContentProcessor/src/libs/utils/azure_credential_utils.py index 3344379c..0e30c658 100644 --- a/src/ContentProcessor/src/libs/utils/azure_credential_utils.py +++ b/src/ContentProcessor/src/libs/utils/azure_credential_utils.py @@ -47,7 +47,7 @@ async def get_async_bearer_token_provider(): Returns: A callable suitable for SDK clients that accept a token provider. """ - credential = await get_async_azure_credential() + credential = get_async_azure_credential() return identity_get_async_bearer_token_provider( credential, "https://cognitiveservices.azure.com/.default" ) @@ -191,11 +191,24 @@ def get_async_azure_credential(): logging.info(f"[AUTH] Using {credential_name} for local development") return credential - # Final fallback to DefaultAzureCredential + app_env = os.getenv("APP_ENV", "prod").lower() + if app_env == "prod": + client_id = os.getenv("AZURE_CLIENT_ID") + if client_id: + logging.info( + "[AUTH] APP_ENV=prod -> using async user-assigned managed identity: %s", + client_id, + ) + return AsyncManagedIdentityCredential(client_id=client_id) + logging.info( + "[AUTH] APP_ENV=prod -> using async system-assigned managed identity" + ) + return AsyncManagedIdentityCredential() + logging.info( - "[AUTH] All async CLI credentials failed - falling back to AsyncDefaultAzureCredential" + "[AUTH] APP_ENV=%s -> falling back to AsyncDefaultAzureCredential", app_env ) - return AsyncDefaultAzureCredential() + return AsyncDefaultAzureCredential() # CodeQL [SM05139] Okay use of DefaultAzureCredential as it is only used in development def validate_azure_authentication() -> dict[str, Any]: diff --git a/src/ContentProcessor/src/libs/utils/credential_util.py b/src/ContentProcessor/src/libs/utils/credential_util.py index 791ab42c..cd03dcb9 100644 --- a/src/ContentProcessor/src/libs/utils/credential_util.py +++ b/src/ContentProcessor/src/libs/utils/credential_util.py @@ -47,7 +47,7 @@ async def get_async_bearer_token_provider(): Returns: A callable suitable for SDK clients that accept a token provider. """ - credential = await get_async_azure_credential() + credential = get_async_azure_credential() return identity_get_async_bearer_token_provider( credential, "https://cognitiveservices.azure.com/.default" ) diff --git a/src/tests/ContentProcessor/utils/test_azure_credential_utils.py b/src/tests/ContentProcessor/utils/test_azure_credential_utils.py index 7f0f88a0..e684b34c 100644 --- a/src/tests/ContentProcessor/utils/test_azure_credential_utils.py +++ b/src/tests/ContentProcessor/utils/test_azure_credential_utils.py @@ -98,7 +98,7 @@ def test_returns_async_user_assigned_with_client_id(self, mock_async_managed): side_effect=Exception("no azd"), ) @patch(f"{MODULE}.AsyncAzureCliCredential", side_effect=Exception("no az")) - @patch.dict("os.environ", {}, clear=True) + @patch.dict("os.environ", {"APP_ENV": "dev"}, clear=True) def test_falls_back_to_async_default( self, mock_async_cli, mock_async_dev_cli, mock_async_default ): diff --git a/src/tests/ContentProcessor/utils/test_azure_credential_utils_extended.py b/src/tests/ContentProcessor/utils/test_azure_credential_utils_extended.py index edd735d8..8f538756 100644 --- a/src/tests/ContentProcessor/utils/test_azure_credential_utils_extended.py +++ b/src/tests/ContentProcessor/utils/test_azure_credential_utils_extended.py @@ -109,6 +109,7 @@ def test_get_async_azure_credential_cli_fallback(self, monkeypatch): for key in ["WEBSITE_SITE_NAME", "AZURE_CLIENT_ID", "MSI_ENDPOINT", "IDENTITY_ENDPOINT", "KUBERNETES_SERVICE_HOST"]: monkeypatch.delenv(key, raising=False) + monkeypatch.setenv("APP_ENV", "dev") with patch('libs.utils.azure_credential_utils.AsyncAzureCliCredential') as mock_cli, \ patch('libs.utils.azure_credential_utils.AsyncAzureDeveloperCliCredential') as mock_azd, \ @@ -149,10 +150,7 @@ async def test_get_async_bearer_token_provider_success(self, monkeypatch): """Test async bearer token provider creation""" monkeypatch.setenv("MSI_ENDPOINT", "http://localhost") - # Create an async mock - from unittest.mock import AsyncMock - - with patch('libs.utils.azure_credential_utils.get_async_azure_credential', new_callable=AsyncMock) as mock_get_cred, \ + with patch('libs.utils.azure_credential_utils.get_async_azure_credential') as mock_get_cred, \ patch('libs.utils.azure_credential_utils.identity_get_async_bearer_token_provider') as mock_provider: mock_credential = Mock()