29
29
__version__ = "1.15.0"
30
30
31
31
logger = logging .getLogger (__name__ )
32
+ _CLOUD_SHELL_USER = "current_cloud_shell_user"
32
33
33
34
34
35
def extract_certs (public_cert_content ):
@@ -951,6 +952,19 @@ def get_accounts(self, username=None):
951
952
# Even in the rare case when an RT is revoked and then removed,
952
953
# acquire_token_silent() would then yield no result,
953
954
# 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
+ })
954
968
return accounts
955
969
956
970
def _find_msal_accounts (self , environment ):
@@ -1109,6 +1123,12 @@ def acquire_token_silent_with_error(
1109
1123
"""
1110
1124
assert isinstance (scopes , list ), "Invalid parameter type"
1111
1125
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" , {}))
1112
1132
correlation_id = msal .telemetry ._get_new_correlation_id ()
1113
1133
if authority :
1114
1134
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(
1201
1221
refresh_reason = msal .telemetry .FORCE_REFRESH # TODO: It could also mean claims_challenge
1202
1222
assert refresh_reason , "It should have been established at this point"
1203
1223
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:
1204
1229
result = _clean_up (self ._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family (
1205
1230
authority , self ._decorate_scope (scopes ), account ,
1206
1231
refresh_reason = refresh_reason , claims_challenge = claims_challenge ,
@@ -1211,6 +1236,18 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1211
1236
logger .exception ("Refresh token failed" ) # Potential AAD outage?
1212
1237
return access_token_from_cache
1213
1238
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
+
1214
1251
def _acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family (
1215
1252
self , authority , scopes , account , ** kwargs ):
1216
1253
query = {
0 commit comments