@@ -1164,8 +1164,8 @@ def test_acquire_token_silent_with_an_empty_cache_should_return_none(self):
1164
1164
# it means MSAL Python is not affected by that.
1165
1165
1166
1166
1167
+ @unittest .skipUnless (broker_available , "AT POP feature is only supported by using broker" )
1167
1168
class PopTestCase (LabBasedTestCase ):
1168
- @unittest .skipUnless (broker_available , "AT POP feature is supported by using broker" )
1169
1169
def test_at_pop_should_contain_pop_scheme_content (self ):
1170
1170
auth_scheme = msal .PopAuthScheme (
1171
1171
http_method = msal .PopAuthScheme .HTTP_GET ,
@@ -1188,6 +1188,92 @@ def test_at_pop_should_contain_pop_scheme_content(self):
1188
1188
self .assertEqual (payload ["p" ], auth_scheme ._url .path )
1189
1189
self .assertEqual (payload ["nonce" ], auth_scheme ._nonce )
1190
1190
1191
+ # TODO: Remove this, as ROPC support is removed by Broker-on-Win
1192
+ def test_at_pop_via_testingsts_service (self ):
1193
+ """Based on https://testingsts.azurewebsites.net/ServerNonce"""
1194
+ self .skipTest ("ROPC support is removed by Broker-on-Win" )
1195
+ auth_scheme = msal .PopAuthScheme (
1196
+ http_method = "POST" ,
1197
+ url = "https://www.Contoso.com/Path1/Path2?queryParam1=a&queryParam2=b" ,
1198
+ nonce = requests .get (
1199
+ # TODO: Could use ".../missing" and then parse its WWW-Authenticate header
1200
+ "https://testingsts.azurewebsites.net/servernonce/get" ).text ,
1201
+ )
1202
+ config = self .get_lab_user (usertype = "cloud" )
1203
+ config ["password" ] = self .get_lab_user_secret (config ["lab_name" ])
1204
+ result = self ._test_username_password (auth_scheme = auth_scheme , ** config )
1205
+ self .assertEqual (result ["token_type" ], "pop" )
1206
+ shr = result ["access_token" ]
1207
+ payload = json .loads (decode_part (result ["access_token" ].split ("." )[1 ]))
1208
+ logger .debug ("AT POP payload = %s" , json .dumps (payload , indent = 2 ))
1209
+ self .assertEqual (payload ["m" ], auth_scheme ._http_method )
1210
+ self .assertEqual (payload ["u" ], auth_scheme ._url .netloc )
1211
+ self .assertEqual (payload ["p" ], auth_scheme ._url .path )
1212
+ self .assertEqual (payload ["nonce" ], auth_scheme ._nonce )
1213
+
1214
+ validation = requests .post (
1215
+ # TODO: This endpoint does not seem to validate the url
1216
+ "https://testingsts.azurewebsites.net/servernonce/validateshr" ,
1217
+ data = {"SHR" : shr },
1218
+ )
1219
+ self .assertEqual (validation .status_code , 200 )
1220
+
1221
+ def test_at_pop_calling_pattern (self ):
1222
+ # The calling pattern was described here:
1223
+ # https://identitydivision.visualstudio.com/DevEx/_git/AuthLibrariesApiReview?path=/PoPTokensProtocol/PoP_API_In_MSAL.md&_a=preview&anchor=proposal-2---optional-isproofofposessionsupportedbyclient-helper-(accepted)
1224
+
1225
+ # It is supposed to call app.is_pop_supported() first,
1226
+ # and then fallback to bearer token code path.
1227
+ # We skip it here because this test case has not yet initialize self.app
1228
+ # assert self.app.is_pop_supported()
1229
+ api_endpoint = "https://20.190.132.47/beta/me"
1230
+ resp = requests .get (api_endpoint , verify = False )
1231
+ self .assertEqual (resp .status_code , 401 , "Initial call should end with an http 401 error" )
1232
+ result = self ._get_shr_pop (** dict (
1233
+ self .get_lab_user (usertype = "cloud" ), # This is generally not the current laptop's default AAD account
1234
+ scope = ["https://graph.microsoft.com/.default" ],
1235
+ auth_scheme = msal .PopAuthScheme (
1236
+ http_method = msal .PopAuthScheme .HTTP_GET ,
1237
+ url = api_endpoint ,
1238
+ nonce = self ._extract_pop_nonce (resp .headers .get ("WWW-Authenticate" )),
1239
+ ),
1240
+ ))
1241
+ resp = requests .get (api_endpoint , verify = False , headers = {
1242
+ "Authorization" : "pop {}" .format (result ["access_token" ]),
1243
+ })
1244
+ if resp .status_code != 200 :
1245
+ # TODO https://teams.microsoft.com/l/message/19:[email protected] /1700184847801?context=%7B%22contextType%22%3A%22chat%22%7D
1246
+ self .skipTest ("We haven't got this end-to-end test case working" )
1247
+ self .assertEqual (resp .status_code , 200 , "POP resource should be accessible" )
1248
+
1249
+ def _extract_pop_nonce (self , www_authenticate ):
1250
+ # This is a hack for testing purpose only. Do not use this in prod.
1251
+ # FYI: There is a www-authenticate package but it falters when encountering realm=""
1252
+ import re
1253
+ found = re .search (r'nonce="(.+?)"' , www_authenticate )
1254
+ if found :
1255
+ return found .group (1 )
1256
+
1257
+ def _get_shr_pop (
1258
+ self , client_id = None , authority = None , scope = None , auth_scheme = None ,
1259
+ ** kwargs ):
1260
+ result = self ._test_acquire_token_interactive (
1261
+ # Lab test users tend to get kicked out from WAM, we use local user to test
1262
+ client_id = client_id ,
1263
+ authority = authority ,
1264
+ scope = scope ,
1265
+ auth_scheme = auth_scheme ,
1266
+ ** kwargs ) # It also tests assertCacheWorksForUser()
1267
+ self .assertEqual (result ["token_source" ], "broker" , "POP is only supported by broker" )
1268
+ self .assertEqual (result ["token_type" ], "pop" )
1269
+ payload = json .loads (decode_part (result ["access_token" ].split ("." )[1 ]))
1270
+ logger .debug ("AT POP payload = %s" , json .dumps (payload , indent = 2 ))
1271
+ self .assertEqual (payload ["m" ], auth_scheme ._http_method )
1272
+ self .assertEqual (payload ["u" ], auth_scheme ._url .netloc )
1273
+ self .assertEqual (payload ["p" ], auth_scheme ._url .path )
1274
+ self .assertEqual (payload ["nonce" ], auth_scheme ._nonce )
1275
+ return result
1276
+
1191
1277
1192
1278
if __name__ == "__main__" :
1193
1279
unittest .main ()
0 commit comments