Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ def _create_client_application(
client_credential = {
"private_key_pfx_path": self._msal_configuration.CERT_PFX_FILE,
}
if self._msal_configuration.SEND_X5C:
client_credential["public_certificate"] = True
Comment thread
rodrigobr-msft marked this conversation as resolved.
elif self._msal_configuration.AUTH_TYPE == AuthTypes.federated_credentials:
mi_client = ManagedIdentityClient(
UserAssignedManagedIdentity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"TENANTID",
"CLIENTSECRET",
"CERTPFXFILE",
"SENDX5C",
"CONNECTIONNAME",
"FEDERATEDCLIENTID",
"FEDERATEDTOKENFILE",
Expand Down Expand Up @@ -63,6 +64,7 @@ class AgentAuthConfiguration:
AUTH_TYPE: The type of authentication to use (microsoft_agents.hosting.core.authorization.auth_types.AuthTypes).
CLIENT_SECRET: The client secret for the Azure AD application (if using client secret authentication).
CERT_PFX_FILE: The path to the PFX certificate file (if using certificate authentication).
SEND_X5C: Whether to include the public certificate in the x5c header for certificate authentication.
CONNECTION_NAME: The name of the connection
FEDERATED_CLIENT_ID: The client ID for federated credentials (if using federated credentials authentication).
SCOPES: The scopes to request
Expand Down Expand Up @@ -91,6 +93,7 @@ class AgentAuthConfiguration:
CLIENT_ID: str | None
CLIENT_SECRET: str | None
CERT_PFX_FILE: str | None
SEND_X5C: bool = False
CONNECTION_NAME: str | None
FEDERATED_CLIENT_ID: str | None
SCOPES: list[str] | None
Expand Down Expand Up @@ -134,6 +137,7 @@ def __init__(
issuers: list[str] | None = None,
validate_issuer: bool | None = None,
federated_token_file: str | None = None,
send_x5c: bool | None = None,
**kwargs: Any,
):

Expand All @@ -149,6 +153,11 @@ def __init__(
self.TENANT_ID = tenant_id or kwargs.get("TENANTID", None)
self.CLIENT_SECRET = client_secret or kwargs.get("CLIENTSECRET", None)
self.CERT_PFX_FILE = cert_pfx_file or kwargs.get("CERTPFXFILE", None)
self.SEND_X5C = coerce_bool(
send_x5c if send_x5c is not None else kwargs.get("SENDX5C", False),
default=False,
name="SENDX5C",
)
self.CONNECTION_NAME = connection_name or kwargs.get("CONNECTIONNAME", None)
self.FEDERATED_CLIENT_ID = federated_client_id or kwargs.get(
"FEDERATEDCLIENTID", None
Expand Down
38 changes: 38 additions & 0 deletions tests/authentication_msal/test_msal_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,44 @@ def test_create_client_application_azure_region_defaults_none(self, mocker):
assert mock_cca.call_args.kwargs["azure_region"] is None


class TestMsalAuthSendX5C:
def test_create_client_application_send_x5c(self, mocker):
config = AgentAuthConfiguration(
auth_type=AuthTypes.certificate,
tenant_id="12345678-1234-1234-1234-123456789abc",
client_id="test-client-id",
cert_pfx_file="test-cert.pfx",
send_x5c=True,
)
mock_cca = mocker.patch(
"microsoft_agents.authentication.msal.msal_auth.ConfidentialClientApplication"
)

MsalAuth(config)._create_client_application()

assert mock_cca.call_args.kwargs["client_credential"] == {
"private_key_pfx_path": "test-cert.pfx",
"public_certificate": True,
}

def test_create_client_application_send_x5c_defaults_false(self, mocker):
config = AgentAuthConfiguration(
auth_type=AuthTypes.certificate,
tenant_id="12345678-1234-1234-1234-123456789abc",
client_id="test-client-id",
cert_pfx_file="test-cert.pfx",
)
mock_cca = mocker.patch(
"microsoft_agents.authentication.msal.msal_auth.ConfidentialClientApplication"
)

MsalAuth(config)._create_client_application()

assert mock_cca.call_args.kwargs["client_credential"] == {
"private_key_pfx_path": "test-cert.pfx",
}


class TestMsalAuthWorkloadIdentity:
def test_create_client_application_reads_projected_token(self, mocker, tmp_path):
token_file = tmp_path / "workload-token"
Expand Down
25 changes: 25 additions & 0 deletions tests/hosting_core/test_auth_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def test_empty_settings(self):
assert auth_config.CLIENT_ID is None
assert auth_config.CLIENT_SECRET is None
assert auth_config.CERT_PFX_FILE is None
assert auth_config.SEND_X5C is False
assert auth_config.FEDERATED_CLIENT_ID is None
assert auth_config.CONNECTION_NAME is None
assert auth_config.AUTHORITY is None
Expand Down Expand Up @@ -162,6 +163,30 @@ def test_workload_identity_token_file_from_kwargs(self):
)
assert "FEDERATEDTOKENFILE" not in auth_config.provider_settings

def test_send_x5c_defaults_false(self):
auth_config = AgentAuthConfiguration()
assert auth_config.SEND_X5C is False

def test_send_x5c_from_parameter(self):
auth_config = AgentAuthConfiguration(send_x5c=True)
assert auth_config.SEND_X5C is True

def test_send_x5c_from_kwargs(self):
auth_config = AgentAuthConfiguration(SENDX5C="true")
assert auth_config.SEND_X5C is True
assert "SENDX5C" not in auth_config.provider_settings

def test_send_x5c_false_string_is_false(self):
auth_config = AgentAuthConfiguration(SENDX5C="false")
assert auth_config.SEND_X5C is False

def test_send_x5c_explicit_false_overrides_kwarg(self):
auth_config = AgentAuthConfiguration(
send_x5c=False,
SENDX5C="true",
)
assert auth_config.SEND_X5C is False

def test_azure_region_from_parameter(self):
auth_config = AgentAuthConfiguration(azure_region="westus")
assert auth_config.AZURE_REGION == "westus"
Expand Down