microsoft-agents-authentication-msal 1.10.0.dev5__tar.gz → 1.10.0.dev7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {microsoft_agents_authentication_msal-1.10.0.dev5/microsoft_agents_authentication_msal.egg-info → microsoft_agents_authentication_msal-1.10.0.dev7}/PKG-INFO +32 -2
- microsoft_agents_authentication_msal-1.10.0.dev7/VERSION.txt +1 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/__init__.py +2 -0
- microsoft_agents_authentication_msal-1.10.0.dev7/microsoft_agents/authentication/msal/connection_settings.py +87 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/msal_auth.py +94 -56
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/msal_connection_manager.py +9 -2
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/msal_token_credential.py +6 -3
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7/microsoft_agents_authentication_msal.egg-info}/PKG-INFO +32 -2
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents_authentication_msal.egg-info/SOURCES.txt +1 -0
- microsoft_agents_authentication_msal-1.10.0.dev7/microsoft_agents_authentication_msal.egg-info/requires.txt +4 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/readme.md +30 -0
- microsoft_agents_authentication_msal-1.10.0.dev5/VERSION.txt +0 -1
- microsoft_agents_authentication_msal-1.10.0.dev5/microsoft_agents_authentication_msal.egg-info/requires.txt +0 -4
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/LICENSE +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/MANIFEST.in +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/errors/__init__.py +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/errors/error_resources.py +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents/authentication/msal/py.typed +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents_authentication_msal.egg-info/dependency_links.txt +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/microsoft_agents_authentication_msal.egg-info/top_level.txt +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/pyproject.toml +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/setup.cfg +0 -0
- {microsoft_agents_authentication_msal-1.10.0.dev5 → microsoft_agents_authentication_msal-1.10.0.dev7}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-authentication-msal
|
|
3
|
-
Version: 1.10.0.
|
|
3
|
+
Version: 1.10.0.dev7
|
|
4
4
|
Summary: A msal-based authentication library for Microsoft Agents
|
|
5
5
|
Author: Microsoft Corporation
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: microsoft-agents-hosting-core==1.10.0.
|
|
18
|
+
Requires-Dist: microsoft-agents-hosting-core==1.10.0.dev7
|
|
19
19
|
Requires-Dist: azure-core
|
|
20
20
|
Requires-Dist: msal>=1.34.0
|
|
21
21
|
Requires-Dist: requests>=2.32.3
|
|
@@ -253,6 +253,36 @@ def start_server(
|
|
|
253
253
|
[...]
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
## Typed Connection Settings
|
|
257
|
+
|
|
258
|
+
`MsalAuth` accepts `Configuration` or `MsalConnectionSettings` as well as the existing
|
|
259
|
+
`AgentAuthConfiguration` input. Use `provider.config` to read bound MSAL settings.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from microsoft_agents.activity.config import Configuration
|
|
263
|
+
from microsoft_agents.authentication.msal import MsalConnectionManager
|
|
264
|
+
|
|
265
|
+
connections = MsalConnectionManager(
|
|
266
|
+
config=Configuration(
|
|
267
|
+
connections={
|
|
268
|
+
"SERVICE_CONNECTION": {
|
|
269
|
+
"settings": {
|
|
270
|
+
"auth_type": "ClientSecret",
|
|
271
|
+
"client_id": "your-client-id",
|
|
272
|
+
"tenant_id": "your-tenant-id",
|
|
273
|
+
"client_secret": "your-client-secret",
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
settings = connections.get_default_connection().config
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Load credentials from your application's secure configuration rather than hardcoding
|
|
283
|
+
them. Legacy constructor calls and the `provider.configuration` compatibility view
|
|
284
|
+
remain supported. Token credentials reuse the provider's bound settings.
|
|
285
|
+
|
|
256
286
|
## Authentication Types
|
|
257
287
|
The M365 Agents SDK in Python supports the following Auth types:
|
|
258
288
|
```python
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.10.0.dev7
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
from .msal_auth import MsalAuth
|
|
5
5
|
from .msal_connection_manager import MsalConnectionManager
|
|
6
6
|
from .msal_token_credential import MsalTokenCredential
|
|
7
|
+
from .connection_settings import MsalConnectionSettings
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
10
|
+
"MsalConnectionSettings",
|
|
9
11
|
"MsalAuth",
|
|
10
12
|
"MsalConnectionManager",
|
|
11
13
|
"MsalTokenCredential",
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
|
|
6
|
+
from microsoft_agents.activity.config import Configuration, xpath
|
|
7
|
+
from microsoft_agents.hosting.core import (
|
|
8
|
+
AgentAuthConfiguration,
|
|
9
|
+
AuthTypes,
|
|
10
|
+
ConnectionSettingsBase,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class MsalConnectionSettings(ConnectionSettingsBase):
|
|
16
|
+
"""MSAL-specific settings, bound independently of inbound JWT policy."""
|
|
17
|
+
|
|
18
|
+
auth_type: str = AuthTypes.client_secret
|
|
19
|
+
client_secret: str | None = field(default=None, repr=False)
|
|
20
|
+
cert_pfx_file: str | None = None
|
|
21
|
+
send_x5c: bool = False
|
|
22
|
+
connection_name: str | None = None
|
|
23
|
+
federated_client_id: str | None = None
|
|
24
|
+
federated_token_file: str | None = None
|
|
25
|
+
idpm_resource: str | None = None
|
|
26
|
+
azure_region: str | None = None
|
|
27
|
+
|
|
28
|
+
def __post_init__(self):
|
|
29
|
+
if self.auth_type == AuthTypes.certificate and not self.cert_pfx_file:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
"CERT_PFX_FILE is required for certificate authentication."
|
|
32
|
+
)
|
|
33
|
+
if (
|
|
34
|
+
self.auth_type == AuthTypes.federated_credentials
|
|
35
|
+
and not self.federated_client_id
|
|
36
|
+
):
|
|
37
|
+
raise ValueError(
|
|
38
|
+
"FEDERATED_CLIENT_ID is required for federated_credentials authentication."
|
|
39
|
+
)
|
|
40
|
+
if (
|
|
41
|
+
self.auth_type == AuthTypes.workload_identity
|
|
42
|
+
and not self.federated_token_file
|
|
43
|
+
):
|
|
44
|
+
raise ValueError(
|
|
45
|
+
"FEDERATED_TOKEN_FILE is required for workload_identity authentication."
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def _get_init_kwargs(cls, config: Configuration) -> dict:
|
|
50
|
+
base = super()._get_init_kwargs(config)
|
|
51
|
+
base.update(
|
|
52
|
+
{
|
|
53
|
+
"auth_type": config.get_str(
|
|
54
|
+
xpath("auth_type"), default=AuthTypes.client_secret
|
|
55
|
+
),
|
|
56
|
+
"client_secret": config.get_strn(xpath("client_secret")),
|
|
57
|
+
"cert_pfx_file": config.get_strn(xpath("cert_pfx_file")),
|
|
58
|
+
"send_x5c": config.get_bool(xpath("send_x5c"), default=False),
|
|
59
|
+
"connection_name": config.get_strn(xpath("connection_name")),
|
|
60
|
+
"federated_client_id": config.get_strn(xpath("federated_client_id")),
|
|
61
|
+
"federated_token_file": config.get_strn(xpath("federated_token_file")),
|
|
62
|
+
"idpm_resource": config.get_strn(xpath("idpm_resource")),
|
|
63
|
+
"azure_region": config.get_strn(
|
|
64
|
+
xpath("azure_region"), xpath("regional_authority")
|
|
65
|
+
),
|
|
66
|
+
}
|
|
67
|
+
)
|
|
68
|
+
return base
|
|
69
|
+
|
|
70
|
+
def _to_agent_auth_config(self) -> AgentAuthConfiguration:
|
|
71
|
+
fields = dict(
|
|
72
|
+
auth_type=self.auth_type,
|
|
73
|
+
client_id=self.client_id,
|
|
74
|
+
tenant_id=self.tenant_id,
|
|
75
|
+
authority=self.authority,
|
|
76
|
+
client_secret=self.client_secret,
|
|
77
|
+
cert_pfx_file=self.cert_pfx_file,
|
|
78
|
+
send_x5c=self.send_x5c,
|
|
79
|
+
connection_name=self.connection_name,
|
|
80
|
+
federated_client_id=self.federated_client_id,
|
|
81
|
+
federated_token_file=self.federated_token_file,
|
|
82
|
+
ALT_BLUEPRINT_NAME=self.alternate_blueprint_connection_name,
|
|
83
|
+
idpm_resource=self.idpm_resource,
|
|
84
|
+
azure_region=self.azure_region,
|
|
85
|
+
scopes=self.scopes,
|
|
86
|
+
)
|
|
87
|
+
return AgentAuthConfiguration(**{**self.extras, **fields})
|
|
@@ -8,7 +8,8 @@ import asyncio
|
|
|
8
8
|
import logging
|
|
9
9
|
import time
|
|
10
10
|
import jwt
|
|
11
|
-
|
|
11
|
+
import warnings
|
|
12
|
+
|
|
12
13
|
from urllib.parse import urlparse, ParseResult as URI
|
|
13
14
|
from msal import (
|
|
14
15
|
ConfidentialClientApplication,
|
|
@@ -23,6 +24,7 @@ from azure.core.credentials_async import AsyncTokenCredential
|
|
|
23
24
|
|
|
24
25
|
from requests import Session
|
|
25
26
|
|
|
27
|
+
from microsoft_agents.activity.config import Configuration
|
|
26
28
|
from microsoft_agents.activity._utils import _DeferredString
|
|
27
29
|
|
|
28
30
|
from microsoft_agents.hosting.core import (
|
|
@@ -31,7 +33,8 @@ from microsoft_agents.hosting.core import (
|
|
|
31
33
|
AgentAuthConfiguration,
|
|
32
34
|
)
|
|
33
35
|
from microsoft_agents.hosting.core.authorization.telemetry import spans
|
|
34
|
-
from
|
|
36
|
+
from .connection_settings import MsalConnectionSettings
|
|
37
|
+
from .errors import authentication_errors
|
|
35
38
|
|
|
36
39
|
logger = logging.getLogger(__name__)
|
|
37
40
|
|
|
@@ -47,29 +50,69 @@ async def _async_acquire_token_for_client(msal_auth_client, *args, **kwargs):
|
|
|
47
50
|
|
|
48
51
|
class MsalAuth(AccessTokenProviderBase):
|
|
49
52
|
|
|
50
|
-
def __init__(
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
msal_configuration: (
|
|
56
|
+
Configuration | MsalConnectionSettings | AgentAuthConfiguration
|
|
57
|
+
),
|
|
58
|
+
):
|
|
51
59
|
"""Initializes the MsalAuth class with the given configuration.
|
|
52
60
|
|
|
53
|
-
:param msal_configuration:
|
|
54
|
-
|
|
55
|
-
|
|
61
|
+
:param msal_configuration: Raw Configuration or bound MsalConnectionSettings.
|
|
62
|
+
AgentAuthConfiguration remains accepted for deprecated compatibility.
|
|
63
|
+
Do not mutate settings or their nested values after passing them in.
|
|
64
|
+
:type config: Configuration | MsalConnectionSettings | AgentAuthConfiguration
|
|
56
65
|
"""
|
|
66
|
+
config = msal_configuration
|
|
67
|
+
if isinstance(config, MsalConnectionSettings):
|
|
68
|
+
self._deprecated_config = None
|
|
69
|
+
_msal_configuration = config
|
|
70
|
+
elif isinstance(config, Configuration):
|
|
71
|
+
self._deprecated_config = None
|
|
72
|
+
_msal_configuration = MsalConnectionSettings.from_configuration(config)
|
|
73
|
+
else:
|
|
74
|
+
warnings.warn(
|
|
75
|
+
"AgentAuthConfiguration is deprecated and will be removed in future versions. "
|
|
76
|
+
"Please pass a config of type Mapping instead.",
|
|
77
|
+
DeprecationWarning,
|
|
78
|
+
stacklevel=2,
|
|
79
|
+
)
|
|
80
|
+
self._deprecated_config = config
|
|
81
|
+
_msal_configuration = MsalConnectionSettings._from_agent_auth_config(config)
|
|
57
82
|
|
|
58
|
-
self._msal_configuration =
|
|
83
|
+
self._msal_configuration = _msal_configuration
|
|
59
84
|
self._msal_auth_client_map: dict[
|
|
60
85
|
str, ConfidentialClientApplication | ManagedIdentityClient
|
|
61
86
|
] = {}
|
|
62
87
|
|
|
63
88
|
# TokenCache is thread-safe and async-safe per MSAL documentation
|
|
64
89
|
self._token_cache = TokenCache()
|
|
65
|
-
logger.debug("Initializing MsalAuth")
|
|
90
|
+
logger.debug("Initializing MsalAuth provider.")
|
|
66
91
|
|
|
67
92
|
@property
|
|
68
93
|
def configuration(self) -> AgentAuthConfiguration:
|
|
69
|
-
"""
|
|
94
|
+
"""Return the deprecated AgentAuthConfiguration compatibility view.
|
|
95
|
+
|
|
96
|
+
Use :attr:`config` for the bound MsalConnectionSettings instead.
|
|
70
97
|
|
|
71
|
-
:return: The
|
|
72
|
-
:rtype:
|
|
98
|
+
:return: The legacy authentication configuration.
|
|
99
|
+
:rtype: AgentAuthConfiguration
|
|
100
|
+
"""
|
|
101
|
+
warnings.warn(
|
|
102
|
+
"Accessing the deprecated configuration. This will be removed in future versions.",
|
|
103
|
+
DeprecationWarning,
|
|
104
|
+
stacklevel=2,
|
|
105
|
+
)
|
|
106
|
+
if self._deprecated_config is None:
|
|
107
|
+
self._deprecated_config = self._msal_configuration._to_agent_auth_config()
|
|
108
|
+
return self._deprecated_config
|
|
109
|
+
|
|
110
|
+
@property
|
|
111
|
+
def config(self) -> MsalConnectionSettings:
|
|
112
|
+
"""Return the bound MSAL settings; nested mutable values remain mutable.
|
|
113
|
+
|
|
114
|
+
:return: The MSAL connection settings.
|
|
115
|
+
:rtype: MsalConnectionSettings
|
|
73
116
|
"""
|
|
74
117
|
return self._msal_configuration
|
|
75
118
|
|
|
@@ -110,12 +153,10 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
110
153
|
"""
|
|
111
154
|
with spans.GetAccessToken(
|
|
112
155
|
scopes,
|
|
113
|
-
self._msal_configuration.
|
|
156
|
+
self._msal_configuration.auth_type,
|
|
114
157
|
):
|
|
115
158
|
logger.debug(
|
|
116
|
-
"Requesting access token for resource:
|
|
117
|
-
resource_url,
|
|
118
|
-
scopes,
|
|
159
|
+
f"Requesting access token for resource: {resource_url}, scopes: {scopes}"
|
|
119
160
|
)
|
|
120
161
|
valid_uri, instance_uri = self._uri_validator(resource_url)
|
|
121
162
|
if not valid_uri:
|
|
@@ -191,7 +232,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
191
232
|
)
|
|
192
233
|
|
|
193
234
|
if "access_token" not in token:
|
|
194
|
-
logger.error("Failed to acquire token on behalf of user.")
|
|
235
|
+
logger.error(f"Failed to acquire token on behalf of user.")
|
|
195
236
|
raise ValueError(
|
|
196
237
|
authentication_errors.FailedToAcquireToken.format(str(token))
|
|
197
238
|
)
|
|
@@ -199,8 +240,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
199
240
|
return token["access_token"]
|
|
200
241
|
|
|
201
242
|
logger.error(
|
|
202
|
-
"On-behalf-of flow is not supported with the current authentication type:
|
|
203
|
-
msal_auth_client.__class__.__name__,
|
|
243
|
+
f"On-behalf-of flow is not supported with the current authentication type: {msal_auth_client.__class__.__name__}"
|
|
204
244
|
)
|
|
205
245
|
raise NotImplementedError(
|
|
206
246
|
authentication_errors.OnBehalfOfFlowNotSupportedAuthType.format(
|
|
@@ -210,49 +250,49 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
210
250
|
|
|
211
251
|
@staticmethod
|
|
212
252
|
def _resolve_authority(
|
|
213
|
-
config:
|
|
253
|
+
config: MsalConnectionSettings, tenant_id: str | None = None
|
|
214
254
|
) -> str:
|
|
215
255
|
tenant_id = MsalAuth._resolve_tenant_id(config, tenant_id)
|
|
216
256
|
if not tenant_id:
|
|
217
257
|
return (
|
|
218
|
-
config.
|
|
219
|
-
or f"https://login.microsoftonline.com/{config.
|
|
258
|
+
config.authority
|
|
259
|
+
or f"https://login.microsoftonline.com/{config.tenant_id}"
|
|
220
260
|
)
|
|
221
261
|
|
|
222
|
-
if config.
|
|
262
|
+
if config.authority:
|
|
223
263
|
return re.sub(
|
|
224
264
|
r"/(?:common|[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})(?=/|$)",
|
|
225
265
|
f"/{tenant_id}",
|
|
226
|
-
config.
|
|
266
|
+
config.authority,
|
|
227
267
|
)
|
|
228
268
|
|
|
229
269
|
return f"https://login.microsoftonline.com/{tenant_id}"
|
|
230
270
|
|
|
231
271
|
@staticmethod
|
|
232
|
-
def _resolve_azure_region(config:
|
|
272
|
+
def _resolve_azure_region(config: MsalConnectionSettings) -> str | None:
|
|
233
273
|
"""Resolves the Azure regional token service (ESTS-R) to use, if configured.
|
|
234
274
|
|
|
235
|
-
:param config: The
|
|
236
|
-
:type config: :class:`microsoft_agents.
|
|
275
|
+
:param config: The bound MSAL connection settings.
|
|
276
|
+
:type config: :class:`microsoft_agents.authentication.msal.MsalConnectionSettings`
|
|
237
277
|
:return: The resolved Azure region or None if not configured.
|
|
238
278
|
:rtype: str | None
|
|
239
279
|
|
|
240
280
|
Returns the configured region only when it is populated and non-whitespace,
|
|
241
281
|
otherwise None so that MSAL falls back to the global token service.
|
|
242
282
|
"""
|
|
243
|
-
azure_region =
|
|
283
|
+
azure_region = config.azure_region
|
|
244
284
|
if azure_region and azure_region.strip():
|
|
245
285
|
return azure_region
|
|
246
286
|
return None
|
|
247
287
|
|
|
248
288
|
@staticmethod
|
|
249
|
-
def _resolve_idpm_resource(config:
|
|
289
|
+
def _resolve_idpm_resource(config: MsalConnectionSettings) -> str:
|
|
250
290
|
"""Resolves the resource URL for Identity Proxy Manager (IDPM) token acquisition.
|
|
251
291
|
|
|
252
292
|
When no resource is configured, defaults to the AzureAdTokenExchange resource.
|
|
253
293
|
Otherwise the configured value must be a valid absolute URI.
|
|
254
294
|
"""
|
|
255
|
-
idpm_resource =
|
|
295
|
+
idpm_resource = config.idpm_resource
|
|
256
296
|
if not idpm_resource:
|
|
257
297
|
return "api://AzureAdTokenExchange/.default"
|
|
258
298
|
|
|
@@ -264,35 +304,35 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
264
304
|
|
|
265
305
|
@staticmethod
|
|
266
306
|
def _resolve_tenant_id(
|
|
267
|
-
config:
|
|
307
|
+
config: MsalConnectionSettings, tenant_id: str | None = None
|
|
268
308
|
) -> str:
|
|
269
309
|
|
|
270
|
-
if not config.
|
|
310
|
+
if not config.tenant_id:
|
|
271
311
|
if tenant_id:
|
|
272
312
|
return tenant_id
|
|
273
313
|
raise ValueError("TENANT_ID is not set in the configuration.")
|
|
274
314
|
|
|
275
|
-
if tenant_id or config.
|
|
315
|
+
if tenant_id or config.tenant_id.lower() == "common":
|
|
276
316
|
return tenant_id
|
|
277
317
|
|
|
278
|
-
return config.
|
|
318
|
+
return config.tenant_id
|
|
279
319
|
|
|
280
320
|
def _create_client_application(
|
|
281
321
|
self, tenant_id: str | None = None
|
|
282
322
|
) -> ConfidentialClientApplication | ManagedIdentityClient:
|
|
283
323
|
|
|
284
|
-
if self._msal_configuration.
|
|
324
|
+
if self._msal_configuration.auth_type in (
|
|
285
325
|
AuthTypes.user_managed_identity,
|
|
286
326
|
AuthTypes.identity_proxy_manager,
|
|
287
327
|
):
|
|
288
328
|
return ManagedIdentityClient(
|
|
289
329
|
UserAssignedManagedIdentity(
|
|
290
|
-
client_id=self._msal_configuration.
|
|
330
|
+
client_id=self._msal_configuration.client_id
|
|
291
331
|
),
|
|
292
332
|
http_client=Session(),
|
|
293
333
|
)
|
|
294
334
|
|
|
295
|
-
elif self._msal_configuration.
|
|
335
|
+
elif self._msal_configuration.auth_type == AuthTypes.system_managed_identity:
|
|
296
336
|
return ManagedIdentityClient(
|
|
297
337
|
SystemAssignedManagedIdentity(),
|
|
298
338
|
http_client=Session(),
|
|
@@ -301,18 +341,18 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
301
341
|
authority = MsalAuth._resolve_authority(self._msal_configuration, tenant_id)
|
|
302
342
|
client_credential = None
|
|
303
343
|
|
|
304
|
-
if self._msal_configuration.
|
|
305
|
-
client_credential = self._msal_configuration.
|
|
306
|
-
elif self._msal_configuration.
|
|
344
|
+
if self._msal_configuration.auth_type == AuthTypes.client_secret:
|
|
345
|
+
client_credential = self._msal_configuration.client_secret
|
|
346
|
+
elif self._msal_configuration.auth_type == AuthTypes.certificate:
|
|
307
347
|
client_credential = {
|
|
308
|
-
"private_key_pfx_path": self._msal_configuration.
|
|
348
|
+
"private_key_pfx_path": self._msal_configuration.cert_pfx_file,
|
|
309
349
|
}
|
|
310
|
-
if self._msal_configuration.
|
|
350
|
+
if self._msal_configuration.send_x5c:
|
|
311
351
|
client_credential["public_certificate"] = True
|
|
312
|
-
elif self._msal_configuration.
|
|
352
|
+
elif self._msal_configuration.auth_type == AuthTypes.federated_credentials:
|
|
313
353
|
mi_client = ManagedIdentityClient(
|
|
314
354
|
UserAssignedManagedIdentity(
|
|
315
|
-
client_id=self._msal_configuration.
|
|
355
|
+
client_id=self._msal_configuration.federated_client_id
|
|
316
356
|
),
|
|
317
357
|
http_client=Session(),
|
|
318
358
|
)
|
|
@@ -323,8 +363,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
323
363
|
)
|
|
324
364
|
if "access_token" not in result:
|
|
325
365
|
logger.error(
|
|
326
|
-
"Failed to acquire token for federated credentials:
|
|
327
|
-
result,
|
|
366
|
+
f"Failed to acquire token for federated credentials: {result}"
|
|
328
367
|
)
|
|
329
368
|
raise ValueError(
|
|
330
369
|
authentication_errors.FailedToAcquireToken.format(
|
|
@@ -334,13 +373,13 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
334
373
|
return result["access_token"]
|
|
335
374
|
|
|
336
375
|
client_credential = {"client_assertion": get_assertion}
|
|
337
|
-
elif self._msal_configuration.
|
|
338
|
-
if not self._msal_configuration.
|
|
376
|
+
elif self._msal_configuration.auth_type == AuthTypes.workload_identity:
|
|
377
|
+
if not self._msal_configuration.federated_token_file:
|
|
339
378
|
raise ValueError(
|
|
340
379
|
"FEDERATED_TOKEN_FILE must be set in configuration."
|
|
341
380
|
)
|
|
342
381
|
|
|
343
|
-
federated_token_file = self._msal_configuration.
|
|
382
|
+
federated_token_file = self._msal_configuration.federated_token_file
|
|
344
383
|
|
|
345
384
|
def get_assertion() -> str:
|
|
346
385
|
with open(federated_token_file, encoding="utf-8") as f:
|
|
@@ -349,15 +388,14 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
349
388
|
client_credential = {"client_assertion": get_assertion}
|
|
350
389
|
else:
|
|
351
390
|
logger.error(
|
|
352
|
-
"Unsupported authentication type:
|
|
353
|
-
self._msal_configuration.AUTH_TYPE,
|
|
391
|
+
f"Unsupported authentication type: {self._msal_configuration.auth_type}"
|
|
354
392
|
)
|
|
355
393
|
raise NotImplementedError(
|
|
356
394
|
str(authentication_errors.AuthenticationTypeNotSupported)
|
|
357
395
|
)
|
|
358
396
|
|
|
359
397
|
return ConfidentialClientApplication(
|
|
360
|
-
client_id=self._msal_configuration.
|
|
398
|
+
client_id=self._msal_configuration.client_id,
|
|
361
399
|
authority=authority,
|
|
362
400
|
client_credential=client_credential,
|
|
363
401
|
azure_region=MsalAuth._resolve_azure_region(self._msal_configuration),
|
|
@@ -368,7 +406,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
368
406
|
) -> str:
|
|
369
407
|
# Create a unique representation for the client based on tenant_id and instance_id
|
|
370
408
|
# instance_id None is for when no agentic instance is associated with the request.
|
|
371
|
-
tenant_id = tenant_id or self._msal_configuration.
|
|
409
|
+
tenant_id = tenant_id or self._msal_configuration.tenant_id
|
|
372
410
|
return f"tenant:{tenant_id}.instance:{instance_id}"
|
|
373
411
|
|
|
374
412
|
def _get_client(
|
|
@@ -388,7 +426,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
388
426
|
result = urlparse(url_str)
|
|
389
427
|
return all([result.scheme, result.netloc]), result
|
|
390
428
|
except AttributeError:
|
|
391
|
-
logger.error("URI parsing error for
|
|
429
|
+
logger.error(f"URI parsing error for {url_str}")
|
|
392
430
|
return False, None
|
|
393
431
|
|
|
394
432
|
def _resolve_scopes_list(self, instance_url: URI, scopes=None) -> list[str]:
|
|
@@ -396,7 +434,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
396
434
|
return scopes
|
|
397
435
|
|
|
398
436
|
temp_list: list[str] = []
|
|
399
|
-
lst = self._msal_configuration.
|
|
437
|
+
lst = self._msal_configuration.scopes or []
|
|
400
438
|
for scope in lst:
|
|
401
439
|
scope_placeholder = scope
|
|
402
440
|
if "{instance}" in scope_placeholder.lower():
|
|
@@ -404,7 +442,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
404
442
|
"{instance}", f"{instance_url.scheme}://{instance_url.hostname}"
|
|
405
443
|
)
|
|
406
444
|
temp_list.append(scope_placeholder)
|
|
407
|
-
logger.debug("Resolved scopes:
|
|
445
|
+
logger.debug(f"Resolved scopes: {temp_list}")
|
|
408
446
|
return temp_list
|
|
409
447
|
|
|
410
448
|
# the call to MSAL is blocking, but in the future we want to create an asyncio task
|
|
@@ -446,7 +484,7 @@ class MsalAuth(AccessTokenProviderBase):
|
|
|
446
484
|
return None
|
|
447
485
|
|
|
448
486
|
if (
|
|
449
|
-
self._msal_configuration.
|
|
487
|
+
self._msal_configuration.auth_type == AuthTypes.identity_proxy_manager
|
|
450
488
|
and isinstance(msal_auth_client, ManagedIdentityClient)
|
|
451
489
|
):
|
|
452
490
|
resource = MsalAuth._resolve_idpm_resource(self._msal_configuration)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
2
2
|
# Licensed under the MIT License.
|
|
3
3
|
|
|
4
|
+
from microsoft_agents.activity.config import Configuration
|
|
4
5
|
from microsoft_agents.hosting.core import (
|
|
5
6
|
AgentAuthConfiguration,
|
|
6
7
|
ConnectionManager,
|
|
@@ -23,15 +24,20 @@ class MsalConnectionManager(ConnectionManager):
|
|
|
23
24
|
self,
|
|
24
25
|
connections_configurations: dict[str, AgentAuthConfiguration] | None = None,
|
|
25
26
|
connections_map: list[dict[str, str]] | None = None,
|
|
27
|
+
*,
|
|
28
|
+
config: Configuration | None = None,
|
|
26
29
|
**kwargs,
|
|
27
30
|
):
|
|
28
31
|
"""
|
|
29
32
|
Initialize the MSAL connection manager.
|
|
30
33
|
|
|
31
|
-
:param connections_configurations:
|
|
34
|
+
:param connections_configurations: Deprecated legacy connection configurations.
|
|
32
35
|
:type connections_configurations: dict[str, :class:`microsoft_agents.hosting.core.AgentAuthConfiguration`]
|
|
33
|
-
:param connections_map:
|
|
36
|
+
:param connections_map: Deprecated list of connection mappings.
|
|
34
37
|
:type connections_map: list[dict[str, str]]
|
|
38
|
+
:param config: Application Configuration containing connections and routing.
|
|
39
|
+
Prefer this over the deprecated positional configuration arguments.
|
|
40
|
+
:type config: Configuration | None
|
|
35
41
|
:raises ValueError: If no service connection configuration is provided.
|
|
36
42
|
"""
|
|
37
43
|
|
|
@@ -39,5 +45,6 @@ class MsalConnectionManager(ConnectionManager):
|
|
|
39
45
|
provider_factory=MsalAuth,
|
|
40
46
|
connections_configurations=connections_configurations,
|
|
41
47
|
connections_map=connections_map,
|
|
48
|
+
config=config,
|
|
42
49
|
**kwargs,
|
|
43
50
|
)
|
|
@@ -8,6 +8,7 @@ from azure.core.credentials_async import AsyncTokenCredential
|
|
|
8
8
|
|
|
9
9
|
from microsoft_agents.hosting.core import AgentAuthConfiguration
|
|
10
10
|
|
|
11
|
+
from .connection_settings import MsalConnectionSettings
|
|
11
12
|
from .msal_auth import MsalAuth
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger(__name__)
|
|
@@ -28,14 +29,16 @@ class MsalTokenCredential(AsyncTokenCredential):
|
|
|
28
29
|
|
|
29
30
|
def __init__(
|
|
30
31
|
self,
|
|
31
|
-
config: AgentAuthConfiguration,
|
|
32
|
+
config: MsalConnectionSettings | AgentAuthConfiguration,
|
|
32
33
|
*,
|
|
33
34
|
provider: MsalAuth | None = None,
|
|
34
35
|
):
|
|
35
36
|
"""Initializes the MsalTokenCredential with the given configuration.
|
|
36
37
|
|
|
37
|
-
:param config:
|
|
38
|
-
|
|
38
|
+
:param config: Bound MSAL settings. AgentAuthConfiguration remains accepted
|
|
39
|
+
for deprecated compatibility.
|
|
40
|
+
:type config: MsalConnectionSettings | AgentAuthConfiguration
|
|
41
|
+
:param provider: An existing provider to reuse instead of constructing one.
|
|
39
42
|
"""
|
|
40
43
|
self._config = config
|
|
41
44
|
self._provider: MsalAuth | None = provider
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: microsoft-agents-authentication-msal
|
|
3
|
-
Version: 1.10.0.
|
|
3
|
+
Version: 1.10.0.dev7
|
|
4
4
|
Summary: A msal-based authentication library for Microsoft Agents
|
|
5
5
|
Author: Microsoft Corporation
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,7 +15,7 @@ Classifier: Operating System :: OS Independent
|
|
|
15
15
|
Requires-Python: >=3.10
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
License-File: LICENSE
|
|
18
|
-
Requires-Dist: microsoft-agents-hosting-core==1.10.0.
|
|
18
|
+
Requires-Dist: microsoft-agents-hosting-core==1.10.0.dev7
|
|
19
19
|
Requires-Dist: azure-core
|
|
20
20
|
Requires-Dist: msal>=1.34.0
|
|
21
21
|
Requires-Dist: requests>=2.32.3
|
|
@@ -253,6 +253,36 @@ def start_server(
|
|
|
253
253
|
[...]
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
## Typed Connection Settings
|
|
257
|
+
|
|
258
|
+
`MsalAuth` accepts `Configuration` or `MsalConnectionSettings` as well as the existing
|
|
259
|
+
`AgentAuthConfiguration` input. Use `provider.config` to read bound MSAL settings.
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from microsoft_agents.activity.config import Configuration
|
|
263
|
+
from microsoft_agents.authentication.msal import MsalConnectionManager
|
|
264
|
+
|
|
265
|
+
connections = MsalConnectionManager(
|
|
266
|
+
config=Configuration(
|
|
267
|
+
connections={
|
|
268
|
+
"SERVICE_CONNECTION": {
|
|
269
|
+
"settings": {
|
|
270
|
+
"auth_type": "ClientSecret",
|
|
271
|
+
"client_id": "your-client-id",
|
|
272
|
+
"tenant_id": "your-tenant-id",
|
|
273
|
+
"client_secret": "your-client-secret",
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
)
|
|
278
|
+
)
|
|
279
|
+
settings = connections.get_default_connection().config
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Load credentials from your application's secure configuration rather than hardcoding
|
|
283
|
+
them. Legacy constructor calls and the `provider.configuration` compatibility view
|
|
284
|
+
remain supported. Token credentials reuse the provider's bound settings.
|
|
285
|
+
|
|
256
286
|
## Authentication Types
|
|
257
287
|
The M365 Agents SDK in Python supports the following Auth types:
|
|
258
288
|
```python
|
|
@@ -5,6 +5,7 @@ pyproject.toml
|
|
|
5
5
|
readme.md
|
|
6
6
|
setup.py
|
|
7
7
|
microsoft_agents/authentication/msal/__init__.py
|
|
8
|
+
microsoft_agents/authentication/msal/connection_settings.py
|
|
8
9
|
microsoft_agents/authentication/msal/msal_auth.py
|
|
9
10
|
microsoft_agents/authentication/msal/msal_connection_manager.py
|
|
10
11
|
microsoft_agents/authentication/msal/msal_token_credential.py
|
|
@@ -229,6 +229,36 @@ def start_server(
|
|
|
229
229
|
[...]
|
|
230
230
|
```
|
|
231
231
|
|
|
232
|
+
## Typed Connection Settings
|
|
233
|
+
|
|
234
|
+
`MsalAuth` accepts `Configuration` or `MsalConnectionSettings` as well as the existing
|
|
235
|
+
`AgentAuthConfiguration` input. Use `provider.config` to read bound MSAL settings.
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from microsoft_agents.activity.config import Configuration
|
|
239
|
+
from microsoft_agents.authentication.msal import MsalConnectionManager
|
|
240
|
+
|
|
241
|
+
connections = MsalConnectionManager(
|
|
242
|
+
config=Configuration(
|
|
243
|
+
connections={
|
|
244
|
+
"SERVICE_CONNECTION": {
|
|
245
|
+
"settings": {
|
|
246
|
+
"auth_type": "ClientSecret",
|
|
247
|
+
"client_id": "your-client-id",
|
|
248
|
+
"tenant_id": "your-tenant-id",
|
|
249
|
+
"client_secret": "your-client-secret",
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
)
|
|
254
|
+
)
|
|
255
|
+
settings = connections.get_default_connection().config
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
Load credentials from your application's secure configuration rather than hardcoding
|
|
259
|
+
them. Legacy constructor calls and the `provider.configuration` compatibility view
|
|
260
|
+
remain supported. Token credentials reuse the provider's bound settings.
|
|
261
|
+
|
|
232
262
|
## Authentication Types
|
|
233
263
|
The M365 Agents SDK in Python supports the following Auth types:
|
|
234
264
|
```python
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.10.0.dev5
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|