microsoft-agents-authentication-msal 0.0.0__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-0.0.0/PKG-INFO +15 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft/agents/authentication/msal/__init__.py +7 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft/agents/authentication/msal/msal_auth.py +179 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft/agents/authentication/msal/msal_connection_manager.py +72 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/PKG-INFO +15 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/SOURCES.txt +10 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/dependency_links.txt +1 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/requires.txt +4 -0
- microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/top_level.txt +1 -0
- microsoft_agents_authentication_msal-0.0.0/pyproject.toml +18 -0
- microsoft_agents_authentication_msal-0.0.0/setup.cfg +4 -0
- microsoft_agents_authentication_msal-0.0.0/setup.py +14 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: microsoft-agents-authentication-msal
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: A msal-based authentication library for Microsoft Agents
|
|
5
|
+
Author: Microsoft Corporation
|
|
6
|
+
Project-URL: Homepage, https://github.com/microsoft/Agents
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Requires-Dist: microsoft-agents-hosting-core==0.0.0
|
|
12
|
+
Requires-Dist: msal>=1.31.1
|
|
13
|
+
Requires-Dist: requests>=2.32.3
|
|
14
|
+
Requires-Dist: cryptography>=44.0.0
|
|
15
|
+
Dynamic: requires-dist
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from urllib.parse import urlparse, ParseResult as URI
|
|
6
|
+
from msal import (
|
|
7
|
+
ConfidentialClientApplication,
|
|
8
|
+
ManagedIdentityClient,
|
|
9
|
+
UserAssignedManagedIdentity,
|
|
10
|
+
SystemAssignedManagedIdentity,
|
|
11
|
+
)
|
|
12
|
+
from requests import Session
|
|
13
|
+
from cryptography.x509 import load_pem_x509_certificate
|
|
14
|
+
from cryptography.hazmat.backends import default_backend
|
|
15
|
+
from cryptography.hazmat.primitives import hashes
|
|
16
|
+
|
|
17
|
+
from microsoft.agents.hosting.core import (
|
|
18
|
+
AuthTypes,
|
|
19
|
+
AccessTokenProviderBase,
|
|
20
|
+
AgentAuthConfiguration,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class MsalAuth(AccessTokenProviderBase):
|
|
27
|
+
|
|
28
|
+
_client_credential_cache = None
|
|
29
|
+
|
|
30
|
+
def __init__(self, msal_configuration: AgentAuthConfiguration):
|
|
31
|
+
self._msal_configuration = msal_configuration
|
|
32
|
+
logger.debug(
|
|
33
|
+
f"Initializing MsalAuth with configuration: {self._msal_configuration}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
async def get_access_token(
|
|
37
|
+
self, resource_url: str, scopes: list[str], force_refresh: bool = False
|
|
38
|
+
) -> str:
|
|
39
|
+
logger.debug(
|
|
40
|
+
f"Requesting access token for resource: {resource_url}, scopes: {scopes}"
|
|
41
|
+
)
|
|
42
|
+
valid_uri, instance_uri = self._uri_validator(resource_url)
|
|
43
|
+
if not valid_uri:
|
|
44
|
+
raise ValueError("Invalid instance URL")
|
|
45
|
+
|
|
46
|
+
local_scopes = self._resolve_scopes_list(instance_uri, scopes)
|
|
47
|
+
msal_auth_client = self._create_client_application()
|
|
48
|
+
|
|
49
|
+
if isinstance(msal_auth_client, ManagedIdentityClient):
|
|
50
|
+
logger.info("Acquiring token using Managed Identity Client.")
|
|
51
|
+
auth_result_payload = msal_auth_client.acquire_token_for_client(
|
|
52
|
+
resource=resource_url
|
|
53
|
+
)
|
|
54
|
+
elif isinstance(msal_auth_client, ConfidentialClientApplication):
|
|
55
|
+
logger.info("Acquiring token using Confidential Client Application.")
|
|
56
|
+
auth_result_payload = msal_auth_client.acquire_token_for_client(
|
|
57
|
+
scopes=local_scopes
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# TODO: Handling token error / acquisition failed
|
|
61
|
+
return auth_result_payload["access_token"]
|
|
62
|
+
|
|
63
|
+
async def aquire_token_on_behalf_of(
|
|
64
|
+
self, scopes: list[str], user_assertion: str
|
|
65
|
+
) -> str:
|
|
66
|
+
"""
|
|
67
|
+
Acquire a token on behalf of a user.
|
|
68
|
+
:param scopes: The scopes for which to get the token.
|
|
69
|
+
:param user_assertion: The user assertion token.
|
|
70
|
+
:return: The access token as a string.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
msal_auth_client = self._create_client_application()
|
|
74
|
+
if isinstance(msal_auth_client, ManagedIdentityClient):
|
|
75
|
+
logger.error(
|
|
76
|
+
"Attempted on-behalf-of flow with Managed Identity authentication."
|
|
77
|
+
)
|
|
78
|
+
raise NotImplementedError(
|
|
79
|
+
"On-behalf-of flow is not supported with Managed Identity authentication."
|
|
80
|
+
)
|
|
81
|
+
elif isinstance(msal_auth_client, ConfidentialClientApplication):
|
|
82
|
+
# TODO: Handling token error / acquisition failed
|
|
83
|
+
return msal_auth_client.acquire_token_on_behalf_of(
|
|
84
|
+
user_assertion=user_assertion, scopes=scopes
|
|
85
|
+
)["access_token"]
|
|
86
|
+
|
|
87
|
+
logger.error(
|
|
88
|
+
f"On-behalf-of flow is not supported with the current authentication type: {msal_auth_client.__class__.__name__}"
|
|
89
|
+
)
|
|
90
|
+
raise NotImplementedError(
|
|
91
|
+
f"On-behalf-of flow is not supported with the current authentication type: {msal_auth_client.__class__.__name__}"
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
def _create_client_application(
|
|
95
|
+
self,
|
|
96
|
+
) -> ManagedIdentityClient | ConfidentialClientApplication:
|
|
97
|
+
msal_auth_client = None
|
|
98
|
+
|
|
99
|
+
if self._msal_configuration.AUTH_TYPE == AuthTypes.user_managed_identity:
|
|
100
|
+
msal_auth_client = ManagedIdentityClient(
|
|
101
|
+
UserAssignedManagedIdentity(
|
|
102
|
+
client_id=self._msal_configuration.CLIENT_ID
|
|
103
|
+
),
|
|
104
|
+
http_client=Session(),
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
elif self._msal_configuration.AUTH_TYPE == AuthTypes.system_managed_identity:
|
|
108
|
+
msal_auth_client = ManagedIdentityClient(
|
|
109
|
+
SystemAssignedManagedIdentity(),
|
|
110
|
+
http_client=Session(),
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
authority_path = self._msal_configuration.TENANT_ID or "botframework.com"
|
|
114
|
+
authority = f"https://login.microsoftonline.com/{authority_path}"
|
|
115
|
+
|
|
116
|
+
if self._client_credential_cache:
|
|
117
|
+
logger.info("Using cached client credentials for MSAL authentication.")
|
|
118
|
+
pass
|
|
119
|
+
elif self._msal_configuration.AUTH_TYPE == AuthTypes.client_secret:
|
|
120
|
+
self._client_credential_cache = self._msal_configuration.CLIENT_SECRET
|
|
121
|
+
elif self._msal_configuration.AUTH_TYPE == AuthTypes.certificate:
|
|
122
|
+
with open(self._msal_configuration.CERT_KEY_FILE) as file:
|
|
123
|
+
logger.info(
|
|
124
|
+
"Loading certificate private key for MSAL authentication."
|
|
125
|
+
)
|
|
126
|
+
private_key = file.read()
|
|
127
|
+
|
|
128
|
+
with open(self._msal_configuration.CERT_PEM_FILE) as file:
|
|
129
|
+
logger.info("Loading public certificate for MSAL authentication.")
|
|
130
|
+
public_certificate = file.read()
|
|
131
|
+
|
|
132
|
+
# Create an X509 object and calculate the thumbprint
|
|
133
|
+
logger.info("Calculating thumbprint for the public certificate.")
|
|
134
|
+
cert = load_pem_x509_certificate(
|
|
135
|
+
data=bytes(public_certificate, "UTF-8"), backend=default_backend()
|
|
136
|
+
)
|
|
137
|
+
thumbprint = cert.fingerprint(hashes.SHA1()).hex()
|
|
138
|
+
|
|
139
|
+
self._client_credential_cache = {
|
|
140
|
+
"thumbprint": thumbprint,
|
|
141
|
+
"private_key": private_key,
|
|
142
|
+
}
|
|
143
|
+
else:
|
|
144
|
+
logger.error(
|
|
145
|
+
f"Unsupported authentication type: {self._msal_configuration.AUTH_TYPE}"
|
|
146
|
+
)
|
|
147
|
+
raise NotImplementedError("Authentication type not supported")
|
|
148
|
+
|
|
149
|
+
msal_auth_client = ConfidentialClientApplication(
|
|
150
|
+
client_id=self._msal_configuration.CLIENT_ID,
|
|
151
|
+
authority=authority,
|
|
152
|
+
client_credential=self._client_credential_cache,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
return msal_auth_client
|
|
156
|
+
|
|
157
|
+
@staticmethod
|
|
158
|
+
def _uri_validator(url_str: str) -> tuple[bool, Optional[URI]]:
|
|
159
|
+
try:
|
|
160
|
+
result = urlparse(url_str)
|
|
161
|
+
return all([result.scheme, result.netloc]), result
|
|
162
|
+
except AttributeError:
|
|
163
|
+
logger.error(f"URI parsing error for {url_str}")
|
|
164
|
+
return False, None
|
|
165
|
+
|
|
166
|
+
def _resolve_scopes_list(self, instance_url: URI, scopes=None) -> list[str]:
|
|
167
|
+
if scopes:
|
|
168
|
+
return scopes
|
|
169
|
+
|
|
170
|
+
temp_list: list[str] = []
|
|
171
|
+
for scope in self._msal_configuration.SCOPES:
|
|
172
|
+
scope_placeholder = scope
|
|
173
|
+
if "{instance}" in scope_placeholder.lower():
|
|
174
|
+
scope_placeholder = scope_placeholder.replace(
|
|
175
|
+
"{instance}", f"{instance_url.scheme}://{instance_url.hostname}"
|
|
176
|
+
)
|
|
177
|
+
temp_list.append(scope_placeholder)
|
|
178
|
+
logger.debug(f"Resolved scopes: {temp_list}")
|
|
179
|
+
return temp_list
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Dict, List, Optional
|
|
2
|
+
from microsoft.agents.hosting.core.authorization import (
|
|
3
|
+
AgentAuthConfiguration,
|
|
4
|
+
AccessTokenProviderBase,
|
|
5
|
+
ClaimsIdentity,
|
|
6
|
+
Connections,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
from .msal_auth import MsalAuth
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MsalConnectionManager(Connections):
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
connections_configurations: Dict[str, AgentAuthConfiguration] = None,
|
|
17
|
+
connections_map: List[Dict[str, str]] = None,
|
|
18
|
+
**kwargs
|
|
19
|
+
):
|
|
20
|
+
self._connections: Dict[str, MsalAuth] = {}
|
|
21
|
+
self._connections_map = connections_map or kwargs.get("CONNECTIONS_MAP", {})
|
|
22
|
+
self._service_connection_configuration: AgentAuthConfiguration = None
|
|
23
|
+
|
|
24
|
+
if connections_configurations:
|
|
25
|
+
for (
|
|
26
|
+
connection_name,
|
|
27
|
+
connection_settings,
|
|
28
|
+
) in connections_configurations.items():
|
|
29
|
+
self._connections[connection_name] = MsalAuth(
|
|
30
|
+
AgentAuthConfiguration(**connection_settings)
|
|
31
|
+
)
|
|
32
|
+
else:
|
|
33
|
+
raw_configurations: Dict[str, Dict] = kwargs.get("CONNECTIONS", {})
|
|
34
|
+
for connection_name, connection_settings in raw_configurations.items():
|
|
35
|
+
parsed_configuration = AgentAuthConfiguration(
|
|
36
|
+
**connection_settings.get("SETTINGS", {})
|
|
37
|
+
)
|
|
38
|
+
self._connections[connection_name] = MsalAuth(parsed_configuration)
|
|
39
|
+
if connection_name == "SERVICE_CONNECTION":
|
|
40
|
+
self._service_connection_configuration = parsed_configuration
|
|
41
|
+
|
|
42
|
+
if not self._connections.get("SERVICE_CONNECTION", None):
|
|
43
|
+
raise ValueError("No service connection configuration provided.")
|
|
44
|
+
|
|
45
|
+
def get_connection(self, connection_name: Optional[str]) -> AccessTokenProviderBase:
|
|
46
|
+
"""
|
|
47
|
+
Get the OAuth connection for the agent.
|
|
48
|
+
"""
|
|
49
|
+
return self._connections.get(connection_name, None)
|
|
50
|
+
|
|
51
|
+
def get_default_connection(self) -> AccessTokenProviderBase:
|
|
52
|
+
"""
|
|
53
|
+
Get the default OAuth connection for the agent.
|
|
54
|
+
"""
|
|
55
|
+
return self._connections.get("SERVICE_CONNECTION", None)
|
|
56
|
+
|
|
57
|
+
def get_token_provider(
|
|
58
|
+
self, claims_identity: ClaimsIdentity, service_url: str
|
|
59
|
+
) -> AccessTokenProviderBase:
|
|
60
|
+
"""
|
|
61
|
+
Get the OAuth token provider for the agent.
|
|
62
|
+
"""
|
|
63
|
+
if not self._connections_map:
|
|
64
|
+
return self.get_default_connection()
|
|
65
|
+
|
|
66
|
+
# TODO: Implement logic to select the appropriate connection based on the connection map
|
|
67
|
+
|
|
68
|
+
def get_default_connection_configuration(self) -> AgentAuthConfiguration:
|
|
69
|
+
"""
|
|
70
|
+
Get the default connection configuration for the agent.
|
|
71
|
+
"""
|
|
72
|
+
return self._service_connection_configuration
|
microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: microsoft-agents-authentication-msal
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: A msal-based authentication library for Microsoft Agents
|
|
5
|
+
Author: Microsoft Corporation
|
|
6
|
+
Project-URL: Homepage, https://github.com/microsoft/Agents
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Requires-Dist: microsoft-agents-hosting-core==0.0.0
|
|
12
|
+
Requires-Dist: msal>=1.31.1
|
|
13
|
+
Requires-Dist: requests>=2.32.3
|
|
14
|
+
Requires-Dist: cryptography>=44.0.0
|
|
15
|
+
Dynamic: requires-dist
|
microsoft_agents_authentication_msal-0.0.0/microsoft_agents_authentication_msal.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
pyproject.toml
|
|
2
|
+
setup.py
|
|
3
|
+
microsoft/agents/authentication/msal/__init__.py
|
|
4
|
+
microsoft/agents/authentication/msal/msal_auth.py
|
|
5
|
+
microsoft/agents/authentication/msal/msal_connection_manager.py
|
|
6
|
+
microsoft_agents_authentication_msal.egg-info/PKG-INFO
|
|
7
|
+
microsoft_agents_authentication_msal.egg-info/SOURCES.txt
|
|
8
|
+
microsoft_agents_authentication_msal.egg-info/dependency_links.txt
|
|
9
|
+
microsoft_agents_authentication_msal.egg-info/requires.txt
|
|
10
|
+
microsoft_agents_authentication_msal.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
microsoft
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "microsoft-agents-authentication-msal"
|
|
7
|
+
dynamic = ["version", "dependencies"]
|
|
8
|
+
description = "A msal-based authentication library for Microsoft Agents"
|
|
9
|
+
authors = [{name = "Microsoft Corporation"}]
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Programming Language :: Python :: 3",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
[project.urls]
|
|
18
|
+
"Homepage" = "https://github.com/microsoft/Agents"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from os import environ
|
|
2
|
+
from setuptools import setup
|
|
3
|
+
|
|
4
|
+
package_version = environ.get("PackageVersion", "0.0.0")
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
version=package_version,
|
|
8
|
+
install_requires=[
|
|
9
|
+
f"microsoft-agents-hosting-core=={package_version}",
|
|
10
|
+
"msal>=1.31.1",
|
|
11
|
+
"requests>=2.32.3",
|
|
12
|
+
"cryptography>=44.0.0",
|
|
13
|
+
],
|
|
14
|
+
)
|