Skip to content

Commit 234bd5a

Browse files
committed
PoC: Silent flow utilizes Cloud Shell IMDS
1 parent a2e2eb6 commit 234bd5a

File tree

1 file changed

+37
-0
lines changed

1 file changed

+37
-0
lines changed

msal/application.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
__version__ = "1.15.0"
3030

3131
logger = logging.getLogger(__name__)
32+
_CLOUD_SHELL_USER = "current_cloud_shell_user"
3233

3334

3435
def extract_certs(public_cert_content):
@@ -951,6 +952,19 @@ def get_accounts(self, username=None):
951952
# Even in the rare case when an RT is revoked and then removed,
952953
# acquire_token_silent() would then yield no result,
953954
# apps would fall back to other acquire methods. This is the standard pattern.
955+
if _is_running_in_cloud_shell():
956+
# In Cloud Shell, user already signed in with an account.
957+
# We pretend we have that account, for acquire_token_silent() to work.
958+
# Note: If user acquire_token_by_xyz() using that account in MSAL later,
959+
# the get_accounts() would return multiple accounts to calling app.
960+
accounts.insert(0, {
961+
"home_account_id": _CLOUD_SHELL_USER,
962+
"environment": "",
963+
"realm": "",
964+
"local_account_id": _CLOUD_SHELL_USER,
965+
"username": "Current Cloud Shell User",
966+
"authority_type": TokenCache.AuthorityType.MSSTS,
967+
})
954968
return accounts
955969

956970
def _find_msal_accounts(self, environment):
@@ -1109,6 +1123,12 @@ def acquire_token_silent_with_error(
11091123
"""
11101124
assert isinstance(scopes, list), "Invalid parameter type"
11111125
self._validate_ssh_cert_input_data(kwargs.get("data", {}))
1126+
if account and account.get("home_account_id") == _CLOUD_SHELL_USER:
1127+
# Since we don't currently store cloud shell tokens in MSAL's cache,
1128+
# we can have a shortcut here, and semantically bypass all those
1129+
# _acquire_token_silent_from_cache_and_possibly_refresh_it()
1130+
return self._acquire_token_by_cloud_shell(
1131+
scopes, data=kwargs.get("data", {}))
11121132
correlation_id = msal.telemetry._get_new_correlation_id()
11131133
if authority:
11141134
warnings.warn("We haven't decided how/if this method will accept authority parameter")
@@ -1201,6 +1221,11 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
12011221
refresh_reason = msal.telemetry.FORCE_REFRESH # TODO: It could also mean claims_challenge
12021222
assert refresh_reason, "It should have been established at this point"
12031223
try:
1224+
## When/if we will store Cloud Shell tokens into MSAL's token cache,
1225+
# then we will add the following code snippet here.
1226+
#if account and account.get("home_account_id") == _CLOUD_SHELL_USER:
1227+
# result = self._acquire_token_by_cloud_shell(scopes, **kwargs)
1228+
#else:
12041229
result = _clean_up(self._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family(
12051230
authority, self._decorate_scope(scopes), account,
12061231
refresh_reason=refresh_reason, claims_challenge=claims_challenge,
@@ -1211,6 +1236,18 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
12111236
logger.exception("Refresh token failed") # Potential AAD outage?
12121237
return access_token_from_cache
12131238

1239+
def _acquire_token_by_cloud_shell(self, scopes, **kwargs):
1240+
kwargs.pop("correlation_id", None) # IMDS does not use correlation_id
1241+
resp = self.http_client.post(
1242+
"http://localhost:50342/oauth2/token",
1243+
data=dict(kwargs.pop("data", {}), resource=" ".join(scopes)),
1244+
headers=dict(kwargs.pop("headers", {}), Metadata="true"),
1245+
**kwargs)
1246+
if resp.status_code >= 300:
1247+
logger.debug("Cloud Shell IMDS error: %s", resp.text)
1248+
# Skip token cache, for now. The Cloud Shell IMDS has its own cache anyway.
1249+
return json.loads(resp.text)
1250+
12141251
def _acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family(
12151252
self, authority, scopes, account, **kwargs):
12161253
query = {

0 commit comments

Comments
 (0)