|
2 | 2 | import json |
3 | 3 | import logging |
4 | 4 |
|
| 5 | +try: |
| 6 | + from unittest.mock import * # Python 3 |
| 7 | +except: |
| 8 | + from mock import * # Need an external mock package |
| 9 | + |
5 | 10 | from msal.application import * |
| 11 | +import msal |
6 | 12 | from tests import unittest |
| 13 | +from tests.test_token_cache import TokenCacheTestCase |
7 | 14 |
|
8 | 15 |
|
9 | 16 | THIS_FOLDER = os.path.dirname(__file__) |
@@ -155,3 +162,80 @@ def test_auth_code(self): |
155 | 162 | error_description=result.get("error_description"))) |
156 | 163 | self.assertCacheWorks(result) |
157 | 164 |
|
| 165 | + |
| 166 | +class TestClientApplicationAcquireTokenSilentFociBehaviors(unittest.TestCase): |
| 167 | + |
| 168 | + def setUp(self): |
| 169 | + self.authority_url = "https://login.microsoftonline.com/common" |
| 170 | + self.authority = msal.authority.Authority(self.authority_url) |
| 171 | + self.scopes = ["s1", "s2"] |
| 172 | + self.uid = "my_uid" |
| 173 | + self.utid = "my_utid" |
| 174 | + self.account = {"home_account_id": "{}.{}".format(self.uid, self.utid)} |
| 175 | + self.frt = "what the frt" |
| 176 | + self.cache = msal.SerializableTokenCache() |
| 177 | + self.cache.add({ # Pre-populate a FRT |
| 178 | + "client_id": "preexisting_family_app", |
| 179 | + "scope": self.scopes, |
| 180 | + "token_endpoint": "{}/oauth2/v2.0/token".format(self.authority_url), |
| 181 | + "response": TokenCacheTestCase.build_response( |
| 182 | + uid=self.uid, utid=self.utid, refresh_token=self.frt, foci="1"), |
| 183 | + }) # The add(...) helper populates correct home_account_id for future searching |
| 184 | + |
| 185 | + def test_unknown_orphan_app_will_attempt_frt_and_not_remove_it(self): |
| 186 | + app = ClientApplication( |
| 187 | + "unknown_orphan", authority=self.authority_url, token_cache=self.cache) |
| 188 | + logger.debug("%s.cache = %s", self.id(), self.cache.serialize()) |
| 189 | + def tester(url, data=None, **kwargs): |
| 190 | + self.assertEqual(self.frt, data.get("refresh_token"), "Should attempt the FRT") |
| 191 | + return Mock(status_code=200, json=Mock(return_value={ |
| 192 | + "error": "invalid_grant", |
| 193 | + "error_description": "Was issued to another client"})) |
| 194 | + app._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( |
| 195 | + self.authority, self.scopes, self.account, post=tester) |
| 196 | + self.assertNotEqual([], app.token_cache.find( |
| 197 | + msal.TokenCache.CredentialType.REFRESH_TOKEN, query={"secret": self.frt}), |
| 198 | + "The FRT should not be removed from the cache") |
| 199 | + |
| 200 | + def test_known_orphan_app_will_skip_frt_and_only_use_its_own_rt(self): |
| 201 | + app = ClientApplication( |
| 202 | + "known_orphan", authority=self.authority_url, token_cache=self.cache) |
| 203 | + rt = "RT for this orphan app. We will check it being used by this test case." |
| 204 | + self.cache.add({ # Populate its RT and AppMetadata, so it becomes a known orphan app |
| 205 | + "client_id": app.client_id, |
| 206 | + "scope": self.scopes, |
| 207 | + "token_endpoint": "{}/oauth2/v2.0/token".format(self.authority_url), |
| 208 | + "response": TokenCacheTestCase.build_response( |
| 209 | + uid=self.uid, utid=self.utid, refresh_token=rt), |
| 210 | + }) |
| 211 | + logger.debug("%s.cache = %s", self.id(), self.cache.serialize()) |
| 212 | + def tester(url, data=None, **kwargs): |
| 213 | + self.assertEqual(rt, data.get("refresh_token"), "Should attempt the RT") |
| 214 | + return Mock(status_code=200, json=Mock(return_value={})) |
| 215 | + app._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( |
| 216 | + self.authority, self.scopes, self.account, post=tester) |
| 217 | + |
| 218 | + def test_unknown_family_app_will_attempt_frt_and_join_family(self): |
| 219 | + def tester(url, data=None, **kwargs): |
| 220 | + self.assertEqual( |
| 221 | + self.frt, data.get("refresh_token"), "Should attempt the FRT") |
| 222 | + return Mock( |
| 223 | + status_code=200, |
| 224 | + json=Mock(return_value=TokenCacheTestCase.build_response( |
| 225 | + uid=self.uid, utid=self.utid, foci="1", access_token="at"))) |
| 226 | + app = ClientApplication( |
| 227 | + "unknown_family_app", authority=self.authority_url, token_cache=self.cache) |
| 228 | + at = app._acquire_token_silent_by_finding_rt_belongs_to_me_or_my_family( |
| 229 | + self.authority, self.scopes, self.account, post=tester) |
| 230 | + logger.debug("%s.cache = %s", self.id(), self.cache.serialize()) |
| 231 | + self.assertEqual("at", at.get("access_token"), "New app should get a new AT") |
| 232 | + app_metadata = app.token_cache.find( |
| 233 | + msal.TokenCache.CredentialType.APP_METADATA, |
| 234 | + query={"client_id": app.client_id}) |
| 235 | + self.assertNotEqual([], app_metadata, "Should record new app's metadata") |
| 236 | + self.assertEqual("1", app_metadata[0].get("family_id"), |
| 237 | + "The new family app should be recorded as in the same family") |
| 238 | + # Known family app will simply use FRT, which is largely the same as this one |
| 239 | + |
| 240 | + # Will not test scenario of app leaving family. Per specs, it won't happen. |
| 241 | + |
0 commit comments