Skip to content

Commit ca67b63

Browse files
updated
1 parent 3218d1a commit ca67b63

File tree

7 files changed

+1075
-1053
lines changed

7 files changed

+1075
-1053
lines changed

msal/managed_identity.py

Lines changed: 31 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ def __init__(
171171
token_cache=None,
172172
http_cache=None,
173173
client_capabilities: Optional[List[str]] = None,
174-
msi_v2_enabled: Optional[bool] = None,
175174
):
176175
"""Create a managed identity client.
177176
@@ -213,17 +212,6 @@ def __init__(
213212
Client capability in Managed Identity is relayed as-is
214213
via ``xms_cc`` parameter on the wire.
215214
216-
:param bool msi_v2_enabled: (optional)
217-
Enable MSI v2 (mTLS PoP) token acquisition.
218-
When True (or when the ``MSAL_ENABLE_MSI_V2`` environment variable
219-
is set to a truthy value), the client will attempt to acquire tokens
220-
using the MSI v2 flow (IMDS /issuecredential + mTLS PoP).
221-
If the MSI v2 flow fails, it automatically falls back to MSI v1.
222-
MSI v2 only applies to Azure VM (IMDS) environments; it is ignored
223-
in other managed identity environments (App Service, Service Fabric,
224-
Azure Arc, etc.).
225-
Defaults to None (disabled unless the env var is set).
226-
227215
Recipe 1: Hard code a managed identity for your app::
228216
229217
import msal, requests
@@ -270,11 +258,6 @@ def __init__(
270258
)
271259
self._token_cache = token_cache or TokenCache()
272260
self._client_capabilities = client_capabilities
273-
# MSI v2 is enabled by the constructor param or the MSAL_ENABLE_MSI_V2 env var
274-
if msi_v2_enabled is None:
275-
env_val = os.environ.get("MSAL_ENABLE_MSI_V2", "").lower()
276-
msi_v2_enabled = env_val in ("1", "true", "yes")
277-
self._msi_v2_enabled = msi_v2_enabled
278261

279262
def acquire_token_for_client(
280263
self,
@@ -309,7 +292,8 @@ def acquire_token_for_client(
309292
Without this flag the legacy IMDS v1 flow is used.
310293
Defaults to False.
311294
312-
This takes precedence over the ``msi_v2_enabled`` constructor parameter.
295+
MSI v2 is used only when both ``mtls_proof_of_possession`` and
296+
``with_attestation_support`` are True.
313297
314298
:param bool with_attestation_support: (optional)
315299
When True (and ``mtls_proof_of_possession`` is also True), attempt
@@ -332,6 +316,27 @@ def acquire_token_for_client(
332316
client_id_in_cache = self._managed_identity.get(
333317
ManagedIdentity.ID, "SYSTEM_ASSIGNED_MANAGED_IDENTITY")
334318
now = time.time()
319+
# MSI v2 is opt-in: use it only when BOTH mtls_proof_of_possession and
320+
# with_attestation_support are explicitly requested by the caller.
321+
# No auto-fallback: if MSI v2 is requested and fails, the error is raised.
322+
use_msi_v2 = bool(mtls_proof_of_possession and with_attestation_support)
323+
324+
if with_attestation_support and not mtls_proof_of_possession:
325+
raise ManagedIdentityError(
326+
"attestation_requires_pop",
327+
"with_attestation_support=True requires mtls_proof_of_possession=True (mTLS PoP)."
328+
)
329+
330+
if use_msi_v2:
331+
from .msi_v2 import obtain_token as _obtain_token_v2
332+
result = _obtain_token_v2(
333+
self._http_client, self._managed_identity, resource,
334+
attestation_enabled=True,
335+
)
336+
if "access_token" in result and "error" not in result:
337+
result[self._TOKEN_SOURCE] = self._TOKEN_SOURCE_IDP
338+
return result
339+
335340
if True: # Attempt cache search even if receiving claims_challenge,
336341
# because we want to locate the existing token (if any) and refresh it
337342
matches = self._token_cache.search(
@@ -366,41 +371,13 @@ def acquire_token_for_client(
366371
break # With a fallback in hand, we break here to go refresh
367372
return access_token_from_cache # It is still good as new
368373
try:
369-
result = None
370-
# Per-call mtls_proof_of_possession takes precedence over the constructor
371-
# default (msi_v2_enabled / MSAL_ENABLE_MSI_V2 env var).
372-
use_msi_v2 = mtls_proof_of_possession or self._msi_v2_enabled
373-
if use_msi_v2:
374-
if mtls_proof_of_possession:
375-
# Explicit per-call request: errors are raised, no fallback to v1
376-
from .msi_v2 import obtain_token as _obtain_token_v2
377-
result = _obtain_token_v2(
378-
self._http_client, self._managed_identity, resource,
379-
attestation_enabled=with_attestation_support)
380-
logger.debug("MSI v2 token acquisition succeeded")
381-
else:
382-
# Legacy constructor flag: swallow errors and fall back to v1
383-
try:
384-
from .msi_v2 import obtain_token as _obtain_token_v2
385-
result = _obtain_token_v2(
386-
self._http_client, self._managed_identity, resource,
387-
attestation_enabled=with_attestation_support)
388-
logger.debug("MSI v2 token acquisition succeeded")
389-
except MsiV2Error as exc:
390-
logger.warning(
391-
"MSI v2 flow failed, falling back to MSI v1: %s", exc)
392-
except Exception as exc: # pylint: disable=broad-except
393-
logger.warning(
394-
"MSI v2 encountered unexpected error, "
395-
"falling back to MSI v1: %s", exc)
396-
if result is None:
397-
result = _obtain_token(
398-
self._http_client, self._managed_identity, resource,
399-
access_token_sha256_to_refresh=hashlib.sha256(
400-
access_token_to_refresh.encode("utf-8")).hexdigest()
401-
if access_token_to_refresh else None,
402-
client_capabilities=self._client_capabilities,
403-
)
374+
result = _obtain_token(
375+
self._http_client, self._managed_identity, resource,
376+
access_token_sha256_to_refresh=hashlib.sha256(
377+
access_token_to_refresh.encode("utf-8")).hexdigest()
378+
if access_token_to_refresh else None,
379+
client_capabilities=self._client_capabilities,
380+
)
404381
if "access_token" in result:
405382
expires_in = result.get("expires_in", 3600)
406383
if "refresh_in" not in result and expires_in >= 7200:
@@ -753,4 +730,4 @@ def _obtain_token_on_arc(http_client, endpoint, resource):
753730
return {
754731
"error": "invalid_request",
755732
"error_description": response.text,
756-
}
733+
}

0 commit comments

Comments
 (0)