2929__version__ = "1.15.0"
3030
3131logger = logging .getLogger (__name__ )
32+ _CLOUD_SHELL_USER = "current_cloud_shell_user"
3233
3334
3435def 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