def test_client_capabilities(): """the credential should configure MSAL for capability CP1 unless AZURE_IDENTITY_DISABLE_CP1 is set""" def send(request, **_): # expecting only the discovery requests triggered by creating an msal.PublicClientApplication # because the cache is empty--the credential shouldn't send a token request return get_discovery_response("https://localhost/tenant") record = AuthenticationRecord("tenant-id", "client_id", "authority", "home_account_id", "username") transport = Mock(send=send) credential = SharedTokenCacheCredential(transport=transport, authentication_record=record, _cache=TokenCache()) with patch("azure.identity._credentials.silent.PublicClientApplication" ) as PublicClientApplication: with pytest.raises(ClientAuthenticationError): # (cache is empty) credential.get_token("scope") assert PublicClientApplication.call_count == 1 _, kwargs = PublicClientApplication.call_args assert kwargs["client_capabilities"] == ["CP1"] credential = SharedTokenCacheCredential(transport=transport, authentication_record=record, _cache=TokenCache()) with patch("azure.identity._credentials.silent.PublicClientApplication" ) as PublicClientApplication: with patch.dict("os.environ", {"AZURE_IDENTITY_DISABLE_CP1": "true"}): with pytest.raises(ClientAuthenticationError): # (cache is empty) credential.get_token("scope") assert PublicClientApplication.call_count == 1 _, kwargs = PublicClientApplication.call_args assert kwargs["client_capabilities"] is None
def test_client_capabilities(): """the credential should configure MSAL for capability CP1 unless AZURE_IDENTITY_DISABLE_CP1 is set""" record = AuthenticationRecord("tenant-id", "client_id", "authority", "home_account_id", "username") transport = Mock(send=Mock(side_effect=Exception("this test mocks MSAL, so no request should be sent"))) credential = SharedTokenCacheCredential( transport=transport, authentication_record=record, _cache=TokenCache() ) with patch(SharedTokenCacheCredential.__module__ + ".PublicClientApplication") as PublicClientApplication: credential._initialize() assert PublicClientApplication.call_count == 1 _, kwargs = PublicClientApplication.call_args assert kwargs["client_capabilities"] == ["CP1"] credential = SharedTokenCacheCredential( transport=transport, authentication_record=record, _cache=TokenCache() ) with patch(SharedTokenCacheCredential.__module__ + ".PublicClientApplication") as PublicClientApplication: with patch.dict("os.environ", {"AZURE_IDENTITY_DISABLE_CP1": "true"}): credential._initialize() assert PublicClientApplication.call_count == 1 _, kwargs = PublicClientApplication.call_args assert kwargs["client_capabilities"] is None
def test_empty_cache(): with pytest.raises(ClientAuthenticationError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache()).get_token("scope") with pytest.raises(ClientAuthenticationError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), username="******").get_token("scope") with pytest.raises(ClientAuthenticationError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), tenant_id="not-cached").get_token("scope") with pytest.raises(ClientAuthenticationError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), tenant_id="not-cached", username="******").get_token("scope")
def test_empty_cache(): """the credential should raise CredentialUnavailableError when the cache is empty""" with pytest.raises(CredentialUnavailableError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache()).get_token("scope") with pytest.raises(CredentialUnavailableError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), username="******").get_token("scope") with pytest.raises(CredentialUnavailableError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), tenant_id="not-cached").get_token("scope") with pytest.raises(CredentialUnavailableError, match=NO_ACCOUNTS): SharedTokenCacheCredential(_cache=TokenCache(), tenant_id="not-cached", username="******").get_token("scope")
def __init__(self, auth_url, **kwargs): # type: (str, Mapping[str, Any]) -> None if not auth_url: raise ValueError("auth_url should be the URL of an OAuth endpoint") super(AuthnClientBase, self).__init__() self._auth_url = auth_url self._cache = TokenCache()
def __init__(self, auth_url, **kwargs): # pylint:disable=unused-argument # type: (str, **Any) -> None if not auth_url: raise ValueError("auth_url should be the URL of an OAuth endpoint") super(AuthnClientBase, self).__init__() self._auth_url = auth_url self._cache = kwargs.get("cache") or TokenCache() # type: TokenCache
def __init__(self, tenant_id, client_id, certificate_path, **kwargs): # type: (str, str, str, **Any) -> None validate_tenant_id(tenant_id) if not certificate_path: raise ValueError( "'certificate_path' must be the path to a PEM file containing an x509 certificate and its private key" ) super(CertificateCredentialBase, self).__init__() password = kwargs.pop("password", None) if isinstance(password, six.text_type): password = password.encode(encoding="utf-8") with open(certificate_path, "rb") as f: pem_bytes = f.read() self._certificate = AadClientCertificate(pem_bytes, password=password) _enable_persistent_cache = kwargs.pop("_enable_persistent_cache", False) if _enable_persistent_cache: allow_unencrypted = kwargs.pop("_allow_unencrypted_cache", False) cache = load_service_principal_cache(allow_unencrypted) else: cache = TokenCache() self._client = self._get_auth_client(tenant_id, client_id, cache=cache, **kwargs) self._client_id = client_id
def test_timeout(): transport = validating_transport( requests=[Request()] * 3, # not validating requests because they're formed by MSAL responses=[ # expected requests: discover tenant, start device code flow, poll for completion mock_response( json_payload={ "authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b" }), mock_response(json_payload={ "device_code": "_", "user_code": "_", "verification_uri": "_" }), mock_response(json_payload={"error": "authorization_pending"}), ], ) credential = DeviceCodeCredential( client_id="_", prompt_callback=Mock(), transport=transport, timeout=0.01, instance_discovery=False, _cache=TokenCache(), ) with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert "timed out" in ex.value.message.lower()
def test_policies_configurable(): policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock()) transport = validating_transport( requests=[Request()] * 3, responses=[ # expected requests: discover tenant, start device code flow, poll for completion get_discovery_response(), mock_response( json_payload={ "device_code": "_", "user_code": "user-code", "verification_uri": "verification-uri", "expires_in": 42, }), mock_response(json_payload=dict( build_aad_response(access_token="**"), scope="scope")), ], ) credential = DeviceCodeCredential(client_id="client-id", prompt_callback=Mock(), policies=[policy], transport=transport, _cache=TokenCache()) credential.get_token("scope") assert policy.on_request.called
def test_device_code_credential(): client_id = "client-id" expected_token = "access-token" user_code = "user-code" verification_uri = "verification-uri" expires_in = 42 transport = validating_transport( requests=[Request()] * 3, # not validating requests because they're formed by MSAL responses=[ # expected requests: discover tenant, start device code flow, poll for completion mock_response( json_payload={ "authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b" }), mock_response( json_payload={ "device_code": "_", "user_code": user_code, "verification_uri": verification_uri, "expires_in": expires_in, }), mock_response(json_payload=dict( build_aad_response( access_token=expected_token, expires_in=expires_in, refresh_token="_", id_token=build_id_token(aud=client_id), ), scope="scope", ), ), ], ) callback = Mock() credential = DeviceCodeCredential( client_id=client_id, prompt_callback=callback, transport=transport, instance_discovery=False, _cache=TokenCache(), ) now = datetime.datetime.utcnow() token = credential.get_token("scope") assert token.token == expected_token # prompt_callback should have been called as documented assert callback.call_count == 1 uri, code, expires_on = callback.call_args[0] assert uri == verification_uri assert code == user_code # validating expires_on exactly would require depending on internals of the credential and # patching time, so we'll be satisfied if expires_on is a datetime at least expires_in # seconds later than our call to get_token assert isinstance(expires_on, datetime.datetime) assert expires_on - now >= datetime.timedelta(seconds=expires_in)
def test_user_agent(): client_id = "client-id" transport = validating_transport( requests=[Request()] * 2 + [Request(required_headers={"User-Agent": USER_AGENT})], responses=[ get_discovery_response(), mock_response( json_payload={ "device_code": "_", "user_code": "user-code", "verification_uri": "verification-uri", "expires_in": 42, }), mock_response(json_payload=dict(build_aad_response( access_token="**", id_token=build_id_token(aud=client_id)), scope="scope")), ], ) credential = DeviceCodeCredential(client_id=client_id, prompt_callback=Mock(), transport=transport, _cache=TokenCache()) credential.get_token("scope")
def test_evicts_invalid_refresh_token(): """when AAD rejects a refresh token, the client should evict that token from its cache""" tenant_id = "tenant-id" client_id = "client-id" invalid_token = "invalid-refresh-token" cache = TokenCache() cache.add({"response": build_aad_response(uid="id1", utid="tid1", access_token="*", refresh_token=invalid_token)}) cache.add({"response": build_aad_response(uid="id2", utid="tid2", access_token="*", refresh_token="...")}) assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN)) == 2 assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN, query={"secret": invalid_token})) == 1 def send(request, **_): assert request.data["refresh_token"] == invalid_token return mock_response(json_payload={"error": "invalid_grant"}, status_code=400) transport = Mock(send=Mock(wraps=send)) client = AadClient(tenant_id, client_id, transport=transport, cache=cache) with pytest.raises(ClientAuthenticationError): client.obtain_token_by_refresh_token(scopes=("scope",), refresh_token=invalid_token) assert transport.send.call_count == 1 assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN)) == 1 assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN, query={"secret": invalid_token})) == 0
async def test_shared_cache(): """The client should return only tokens associated with its own client_id""" client_id_a = "client-id-a" client_id_b = "client-id-b" scope = "scope" expected_token = "***" tenant_id = "tenant" authority = "https://localhost/" + tenant_id cache = TokenCache() cache.add({ "response": build_aad_response(access_token=expected_token), "client_id": client_id_a, "scope": [scope], "token_endpoint": "/".join((authority, tenant_id, "oauth2/v2.0/token")), }) common_args = dict(authority=authority, cache=cache, tenant_id=tenant_id) client_a = AadClient(client_id=client_id_a, **common_args) client_b = AadClient(client_id=client_id_b, **common_args) # A has a cached token token = client_a.get_cached_access_token([scope]) assert token.token == expected_token # which B shouldn't return assert client_b.get_cached_access_token([scope]) is None
def test_access_token_caching(): """'get_token' shouldn't return other users' access tokens""" scope = "scope" forbidden_access_token = "don't use me" expected_access_token = "access token" my_refresh_token = "my refresh token" your_refresh_token = "your refresh token" me = "me" uid = "uidme" utid = "utidme" cache = TokenCache() cache.add( get_account_event( username=me, uid=uid, utid=utid, refresh_token=my_refresh_token, access_token=forbidden_access_token, scopes=[scope], )) you = "you" uid = "uidyou" utid = "utidyou" cache.add( get_account_event( username=you, uid=uid, utid=utid, refresh_token=your_refresh_token, access_token=expected_access_token, scopes=[scope], ))
def test_user_agent(): client_id = "client-id" transport = validating_transport( requests=[ Request(), Request(required_headers={"User-Agent": USER_AGENT}) ], responses=[ get_discovery_response(), mock_response(json_payload=build_aad_response( access_token="**", id_token=build_id_token(aud=client_id))), ], ) # mock local server fakes successful authentication by immediately returning a well-formed response oauth_state = "oauth-state" auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) credential = InteractiveBrowserCredential(client_id=client_id, transport=transport, _server_class=server_class, _cache=TokenCache()) with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state): credential.get_token("scope")
def test_claims_challenge(): """get_token should pass any claims challenge to MSAL token acquisition APIs""" expected_claims = '{"access_token": {"essential": "true"}' record = AuthenticationRecord("tenant-id", "client_id", "authority", "home_account_id", "username") msal_app = Mock() msal_app.get_accounts.return_value = [{ "home_account_id": record.home_account_id }] msal_app.acquire_token_silent_with_error.return_value = dict( build_aad_response(access_token="**", id_token=build_id_token())) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = SharedTokenCacheCredential(transport=transport, authentication_record=record, _cache=TokenCache()) with patch( SharedTokenCacheCredential.__module__ + ".PublicClientApplication", lambda *_, **__: msal_app): credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_silent_with_error.call_count == 1 args, kwargs = msal_app.acquire_token_silent_with_error.call_args assert kwargs["claims_challenge"] == expected_claims
def test_no_browser(): transport = validating_transport(requests=[Request()] * 2, responses=[get_discovery_response()] * 2) credential = InteractiveBrowserCredential( client_id="client-id", server_class=Mock(), transport=transport, _cache=TokenCache() ) with pytest.raises(ClientAuthenticationError, match=r".*browser.*"): credential.get_token("scope")
def test_policies_configurable(): policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock()) client_id = "client-id" transport = validating_transport( requests=[Request()] * 2, responses=[ get_discovery_response(), mock_response(json_payload=build_aad_response( access_token="**", id_token=build_id_token(aud=client_id))), ], ) # mock local server fakes successful authentication by immediately returning a well-formed response oauth_state = "oauth-state" auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) credential = InteractiveBrowserCredential(policies=[policy], client_id=client_id, transport=transport, _server_class=server_class, _cache=TokenCache()) with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state): credential.get_token("scope") assert policy.on_request.called
def test_interactive_credential_timeout(): # mock transport handles MSAL's tenant discovery transport = Mock( send=lambda _, **__: mock_response( json_payload={"authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b"} ) ) # mock local server blocks long enough to exceed the timeout timeout = 0.01 server_instance = Mock(wait_for_redirect=functools.partial(time.sleep, timeout + 0.01)) server_class = Mock(return_value=server_instance) credential = InteractiveBrowserCredential( client_id="guid", server_class=server_class, timeout=timeout, transport=transport, instance_discovery=False, # kwargs are passed to MSAL; this one prevents an AAD verification request _cache=TokenCache(), ) with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert "timed out" in ex.value.message.lower()
async def test_persistent_cache_multiple_clients(cert_path, cert_password): """the credential shouldn't use tokens issued to other service principals""" access_token_a = "token a" access_token_b = "not " + access_token_a transport_a = async_validating_transport( requests=[Request()], responses=[mock_response(json_payload=build_aad_response(access_token=access_token_a))] ) transport_b = async_validating_transport( requests=[Request()], responses=[mock_response(json_payload=build_aad_response(access_token=access_token_b))] ) cache = TokenCache() with patch("azure.identity._internal.persistent_cache._load_persistent_cache") as mock_cache_loader: mock_cache_loader.return_value = Mock(wraps=cache) credential_a = CertificateCredential( "tenant", "client-a", cert_path, password=cert_password, enable_persistent_cache=True, transport=transport_a ) assert mock_cache_loader.call_count == 1, "credential should load the persistent cache" credential_b = CertificateCredential( "tenant", "client-b", cert_path, password=cert_password, enable_persistent_cache=True, transport=transport_b ) assert mock_cache_loader.call_count == 2, "credential should load the persistent cache" # A caches a token scope = "scope" token_a = await credential_a.get_token(scope) assert token_a.token == access_token_a assert transport_a.send.call_count == 1 # B should get a different token for the same scope token_b = await credential_b.get_token(scope) assert token_b.token == access_token_b assert transport_b.send.call_count == 1
def __init__(self, tenant_id, client_id, authority=None, cache=None, **kwargs): # type: (str, str, Optional[str], Optional[TokenCache], **Any) -> None authority = normalize_authority(authority) if authority else get_default_authority() self._token_endpoint = "/".join((authority, tenant_id, "oauth2/v2.0/token")) self._cache = cache or TokenCache() self._client_id = client_id self._pipeline = self._build_pipeline(**kwargs)
def __init__(self, tenant_id, client_id, client_secret, **kwargs): # type: (str, str, str, **Any) -> None if not client_id: raise ValueError( "client_id should be the id of an Azure Active Directory application" ) if not client_secret: raise ValueError( "secret should be an Azure Active Directory application's client secret" ) if not tenant_id: raise ValueError( "tenant_id should be an Azure Active Directory tenant's id (also called its 'directory id')" ) enable_persistent_cache = kwargs.pop("enable_persistent_cache", False) if enable_persistent_cache: allow_unencrypted = kwargs.pop("allow_unencrypted_cache", False) cache = load_service_principal_cache(allow_unencrypted) else: cache = TokenCache() self._client = self._get_auth_client(tenant_id, client_id, cache=cache, **kwargs) self._client_id = client_id self._secret = client_secret
def test_timeout(): """get_token should raise ClientAuthenticationError when the server times out without receiving a redirect""" timeout = 0.01 class GuaranteedTimeout(AuthCodeRedirectServer, object): def handle_request(self): time.sleep(timeout + 0.01) super(GuaranteedTimeout, self).handle_request() # mock transport handles MSAL's tenant discovery transport = Mock(send=lambda _, **__: mock_response( json_payload={ "authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b" })) credential = InteractiveBrowserCredential(timeout=timeout, transport=transport, _cache=TokenCache(), _server_class=GuaranteedTimeout) with patch(WEBBROWSER_OPEN, lambda _: True): with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert "timed out" in ex.value.message.lower()
def test_authenticate(): client_id = "client-id" environment = "localhost" issuer = "https://" + environment tenant_id = "some-tenant" authority = issuer + "/" + tenant_id access_token = "***" scope = "scope" # mock AAD response with id token object_id = "object-id" home_tenant = "home-tenant-id" username = "******" id_token = build_id_token(aud=client_id, iss=issuer, object_id=object_id, tenant_id=home_tenant, username=username) auth_response = build_aad_response(uid=object_id, utid=home_tenant, access_token=access_token, refresh_token="**", id_token=id_token) transport = validating_transport( requests=[Request(url_substring=issuer)] * 4, responses=[get_discovery_response(authority)] * 2 # instance and tenant discovery + [ mock_response( # start device code flow json_payload={ "device_code": "_", "user_code": "user-code", "verification_uri": "verification-uri", "expires_in": 42, } ), mock_response(json_payload=dict(auth_response, scope=scope)), # poll for completion ], ) credential = DeviceCodeCredential( client_id, prompt_callback=Mock(), # prevent credential from printing to stdout transport=transport, authority=environment, tenant_id=tenant_id, _cache=TokenCache(), ) record = credential.authenticate(scopes=(scope, )) # credential should have a cached access token for the scope used in authenticate token = credential.get_token(scope) assert token.token == access_token assert record.authority == environment assert record.home_account_id == object_id + "." + home_tenant assert record.tenant_id == home_tenant assert record.username == username
def test_authority(authority): """the credential should accept an authority, with or without scheme, as an argument or environment variable""" parsed_authority = urlparse(authority) expected_netloc = parsed_authority.netloc or authority # "localhost" parses to netloc "", path "localhost" class MockCredential(SharedTokenCacheCredential): def _get_auth_client(self, authority=None, **kwargs): actual = urlparse(authority) assert actual.scheme == "https" assert actual.netloc == expected_netloc transport = Mock(send=Mock(side_effect=Exception("credential shouldn't send a request"))) MockCredential(_cache=TokenCache(), authority=authority, transport=transport) with patch.dict("os.environ", {EnvironmentVariables.AZURE_AUTHORITY_HOST: authority}, clear=True): MockCredential(_cache=TokenCache(), authority=authority, transport=transport)
def test_disable_automatic_authentication(): """When configured for strict silent auth, the credential should raise when silent auth fails""" empty_cache = TokenCache() # empty cache makes silent auth impossible transport = Mock(send=Mock(side_effect=Exception("no request should be sent"))) credential = DeviceCodeCredential("client-id", disable_automatic_authentication=True, transport=transport, _cache=empty_cache) with pytest.raises(AuthenticationRequiredError): credential.get_token("scope")
def __init__( self, client_id="...", request_token=None, cache=None, msal_app_factory=None, transport=None, **kwargs ): self._msal_app_factory = msal_app_factory self._request_token_impl = request_token or Mock() transport = transport or Mock(send=Mock(side_effect=Exception("credential shouldn't send a request"))) super(MockCredential, self).__init__( client_id=client_id, _cache=cache or TokenCache(), transport=transport, **kwargs )
def test_persistent_cache_multiple_clients(cert_path, cert_password): """the credential shouldn't use tokens issued to other service principals""" access_token_a = "token a" access_token_b = "not " + access_token_a transport_a = msal_validating_transport( requests=[Request()], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_a)) ]) transport_b = msal_validating_transport( requests=[Request()], responses=[ mock_response(json_payload=build_aad_response( access_token=access_token_b)) ]) cache = TokenCache() with patch( "azure.identity._internal.msal_credentials._load_persistent_cache" ) as mock_cache_loader: mock_cache_loader.return_value = Mock(wraps=cache) credential_a = CertificateCredential( "tenant", "client-a", cert_path, password=cert_password, transport=transport_a, cache_persistence_options=TokenCachePersistenceOptions(), ) assert mock_cache_loader.call_count == 1, "credential should load the persistent cache" credential_b = CertificateCredential( "tenant", "client-b", cert_path, password=cert_password, transport=transport_b, cache_persistence_options=TokenCachePersistenceOptions(), ) assert mock_cache_loader.call_count == 2, "credential should load the persistent cache" # A caches a token scope = "scope" token_a = credential_a.get_token(scope) assert token_a.token == access_token_a assert transport_a.send.call_count == 3 # two MSAL discovery requests, one token request # B should get a different token for the same scope token_b = credential_b.get_token(scope) assert token_b.token == access_token_b assert transport_b.send.call_count == 3 assert len(cache.find(TokenCache.CredentialType.ACCESS_TOKEN)) == 2
def test_writes_to_cache(): """the credential should write tokens it acquires to the cache""" scope = "scope" expected_access_token = "access token" first_refresh_token = "first refresh token" second_refresh_token = "second refresh token" username = "******" uid = "uid" utid = "utid" account = get_account_event(username=username, uid=uid, utid=utid, refresh_token=first_refresh_token) cache = TokenCache() cache.add(account) transport = validating_transport( requests=[Request(required_data={"refresh_token": first_refresh_token})], # credential redeems refresh token responses=[ mock_response( json_payload=build_aad_response( # AAD responds with an access token and new refresh token uid=uid, utid=utid, access_token=expected_access_token, refresh_token=second_refresh_token, id_token=build_id_token( aud=DEVELOPER_SIGN_ON_CLIENT_ID, object_id=uid, tenant_id=utid, username=username ), ) ) ], ) credential = SharedTokenCacheCredential(_cache=cache, transport=transport) token = credential.get_token(scope) assert token.token == expected_access_token # access token should be in the cache, and another instance should retrieve it credential = SharedTokenCacheCredential( _cache=cache, transport=Mock(send=Mock(side_effect=Exception("the credential should return a cached token"))) ) token = credential.get_token(scope) assert token.token == expected_access_token # and the credential should have updated the cached refresh token second_access_token = "second access token" transport = validating_transport( requests=[Request(required_data={"refresh_token": second_refresh_token})], responses=[mock_response(json_payload=build_aad_response(access_token=second_access_token))], ) credential = SharedTokenCacheCredential(_cache=cache, transport=transport) token = credential.get_token("some other " + scope) assert token.token == second_access_token # verify the credential didn't add a new cache entry assert len(cache.find(TokenCache.CredentialType.REFRESH_TOKEN)) == 1
def test_authentication_record_empty_cache(): record = AuthenticationRecord("tenant_id", "client_id", "authority", "home_account_id", "username") transport = Mock( side_effect=Exception("the credential shouldn't send a request")) credential = SharedTokenCacheCredential(authentication_record=record, transport=transport, _cache=TokenCache()) with pytest.raises(CredentialUnavailableError): credential.get_token("scope")