29
29
__version__ = "1.16.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 ):
@@ -967,6 +968,19 @@ def get_accounts(self, username=None):
967
968
# Even in the rare case when an RT is revoked and then removed,
968
969
# acquire_token_silent() would then yield no result,
969
970
# apps would fall back to other acquire methods. This is the standard pattern.
971
+ if _is_running_in_cloud_shell ():
972
+ # In Cloud Shell, user already signed in with an account.
973
+ # We pretend we have that account, for acquire_token_silent() to work.
974
+ # Note: If user acquire_token_by_xyz() using that account in MSAL later,
975
+ # the get_accounts() would return multiple accounts to calling app.
976
+ accounts .insert (0 , {
977
+ "home_account_id" : _CLOUD_SHELL_USER ,
978
+ "environment" : "" ,
979
+ "realm" : "" ,
980
+ "local_account_id" : _CLOUD_SHELL_USER ,
981
+ "username" : "Current Cloud Shell User" ,
982
+ "authority_type" : TokenCache .AuthorityType .MSSTS ,
983
+ })
970
984
return accounts
971
985
972
986
def _find_msal_accounts (self , environment ):
@@ -1125,6 +1139,12 @@ def acquire_token_silent_with_error(
1125
1139
"""
1126
1140
assert isinstance (scopes , list ), "Invalid parameter type"
1127
1141
self ._validate_ssh_cert_input_data (kwargs .get ("data" , {}))
1142
+ if account and account .get ("home_account_id" ) == _CLOUD_SHELL_USER :
1143
+ # Since we don't currently store cloud shell tokens in MSAL's cache,
1144
+ # we can have a shortcut here, and semantically bypass all those
1145
+ # _acquire_token_silent_from_cache_and_possibly_refresh_it()
1146
+ return self ._acquire_token_by_cloud_shell (
1147
+ scopes , data = kwargs .get ("data" , {}))
1128
1148
correlation_id = msal .telemetry ._get_new_correlation_id ()
1129
1149
if authority :
1130
1150
warnings .warn ("We haven't decided how/if this method will accept authority parameter" )
@@ -1217,6 +1237,11 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1217
1237
refresh_reason = msal .telemetry .FORCE_REFRESH # TODO: It could also mean claims_challenge
1218
1238
assert refresh_reason , "It should have been established at this point"
1219
1239
try :
1240
+ ## When/if we will store Cloud Shell tokens into MSAL's token cache,
1241
+ # then we will add the following code snippet here.
1242
+ #if account and account.get("home_account_id") == _CLOUD_SHELL_USER:
1243
+ # result = self._acquire_token_by_cloud_shell(scopes, **kwargs)
1244
+ #else:
1220
1245
result = _clean_up (self ._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family (
1221
1246
authority , self ._decorate_scope (scopes ), account ,
1222
1247
refresh_reason = refresh_reason , claims_challenge = claims_challenge ,
@@ -1229,6 +1254,24 @@ def _acquire_token_silent_from_cache_and_possibly_refresh_it(
1229
1254
raise # We choose to bubble up the exception
1230
1255
return access_token_from_cache
1231
1256
1257
+ def _acquire_token_by_cloud_shell (self , scopes , ** kwargs ):
1258
+ kwargs .pop ("correlation_id" , None ) # IMDS does not use correlation_id
1259
+ resp = self .http_client .post (
1260
+ "http://localhost:50342/oauth2/token" ,
1261
+ data = dict (kwargs .pop ("data" , {}), resource = " " .join (scopes )),
1262
+ headers = dict (kwargs .pop ("headers" , {}), Metadata = "true" ),
1263
+ ** kwargs )
1264
+ if resp .status_code >= 300 :
1265
+ logger .debug ("Cloud Shell IMDS error: %s" , resp .text )
1266
+ cs_error = json .loads (resp .text ).get ("error" , {})
1267
+ return {k : v for k , v in {
1268
+ "error" : cs_error .get ("code" ),
1269
+ "error_description" : cs_error .get ("message" ),
1270
+ }.items () if v }
1271
+ else :
1272
+ # Skip token cache, for now. Cloud Shell IMDS has its own cache anyway.
1273
+ return json .loads (resp .text )
1274
+
1232
1275
def _acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family (
1233
1276
self , authority , scopes , account , ** kwargs ):
1234
1277
query = {
0 commit comments