def __init__(self, vault_url, credential=None): ''' Parameters: vault_url (str) – RL of the vault the client will access. This is also called the vault's "DNS Name". credential (str) – An object which can provide an access token for the vault, such as a service principal credential Example: .. code-block:: python import pynomial as pyn client = pyn.KeyVaultclient(vault_url='https://keyvaulttestinstance.vault.azure.net/') retrieved_secret = client.get_secret('MyPassword') retrieved_secret.value .. code-block:: python import pynomial as pyn credential = pyn.SPCredential() client = pyn.KeyVaultclient(vault_url='https://keyvaulttestinstance.vault.azure.net/', credential=credential) retrieved_secret = client.get_secret('MyPassword').value ''' from azure.identity import InteractiveBrowserCredential if credential is None: credential = InteractiveBrowserCredential() self.token = credential.get_token() self = super().__init__(vault_url=vault_url, credential=credential)
def test_login_hint(): expected_username = "******" auth_code_response = {"code": "authorization-code", "state": ["..."]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) mock_msal_app = Mock( acquire_token_by_auth_code_flow=Mock( return_value=msal_acquire_token_result), initiate_auth_code_flow=Mock( return_value={"auth_uri": "http://localhost"}), ) credential = InteractiveBrowserCredential(_server_class=server_class, transport=transport, login_hint=expected_username) with patch("msal.PublicClientApplication", Mock(return_value=mock_msal_app)): with patch(WEBBROWSER_OPEN, lambda _: True): credential.authenticate(scopes=["scope"]) assert mock_msal_app.initiate_auth_code_flow.call_count == 1 _, kwargs = mock_msal_app.initiate_auth_code_flow.call_args assert kwargs["login_hint"] == expected_username
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_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_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_cannot_bind_port(): """get_token should raise CredentialUnavailableError when the redirect listener can't bind a port""" credential = InteractiveBrowserCredential(_server_class=Mock( side_effect=socket.error)) with pytest.raises(CredentialUnavailableError): credential.get_token("scope")
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", client_secret="secret", server_class=server_class, timeout=timeout, transport=transport, instance_discovery= False, # kwargs are passed to MSAL; this one prevents an AAD verification request ) with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert "timed out" in ex.value.message.lower()
def get_credential(self, auth_record=None, user_client_id=None, tenant_id=None) -> InteractiveBrowserCredential: ''' Raises ------ ValueError If PyGObject is not installed in the host Linux OS. ''' profile = read_profile() user_cloud = profile.get('cloud', None) cloud_authority = user_cloud.get('azure_ad_endpoint', None) authority = cloud_authority or DEFAULT_AUTHORITY client_id = user_client_id or DEFAULT_CLIENT_ID # Once a user is authenticated they get an auth_record object which will have the authority and client_id # therefore we don't need to pass authority and client_id to InteractiveBrowserCredential. if auth_record: return InteractiveBrowserCredential( enable_persistent_cache=True, allow_unencrypted_cache=False, authentication_record=auth_record, ) # Passes authority and client_id when the user doesn't have an auth record return InteractiveBrowserCredential( authority=authority, client_id=client_id, tenant_id=tenant_id, enable_persistent_cache=True, allow_unencrypted_cache=False, )
def test_disable_automatic_authentication(): """When configured for strict silent auth, the credential should raise when silent auth fails""" transport = Mock(send=Mock(side_effect=Exception("no request should be sent"))) credential = InteractiveBrowserCredential(disable_automatic_authentication=True, transport=transport) with patch(WEBBROWSER_OPEN, Mock(side_effect=Exception("credential shouldn't try interactive authentication"))): with pytest.raises(AuthenticationRequiredError): credential.get_token("scope")
def test_cannot_bind_redirect_uri(): """When a user specifies a redirect URI, the credential shouldn't attempt to bind another""" server = Mock(side_effect=socket.error) credential = InteractiveBrowserCredential(redirect_uri="http://localhost:42", _server_class=server) with pytest.raises(CredentialUnavailableError): credential.get_token("scope") server.assert_called_once_with("localhost", 42, timeout=ANY)
def test_tenant_id_validation(): """The credential should raise ValueError when given an invalid tenant_id""" valid_ids = {"c878a2ab-8ef4-413b-83a0-199afb84d7fb", "contoso.onmicrosoft.com", "organizations", "common"} for tenant in valid_ids: InteractiveBrowserCredential(tenant_id=tenant) invalid_ids = {"my tenant", "my_tenant", "/", "\\", '"my-tenant"', "'my-tenant'"} for tenant in invalid_ids: with pytest.raises(ValueError): InteractiveBrowserCredential(tenant_id=tenant)
def test_policies_configurable(): # the policy raises an exception so this test can run without authenticating i.e. opening a browser expected_message = "test_policies_configurable" policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock(side_effect=Exception(expected_message))) credential = InteractiveBrowserCredential(policies=[policy]) with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert expected_message in ex.value.message assert policy.on_request.called
def test_no_browser(): """The credential should raise CredentialUnavailableError when it can't open a browser""" transport = validating_transport(requests=[Request()] * 2, responses=[get_discovery_response()] * 2) credential = InteractiveBrowserCredential(client_id="client-id", _server_class=Mock(), transport=transport) with patch(InteractiveBrowserCredential.__module__ + "._open_browser", lambda _: False): with pytest.raises(CredentialUnavailableError, match=r".*browser.*"): credential.get_token("scope")
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)] * 3, responses=[get_discovery_response(authority)] * 2 + [mock_response(json_payload=auth_response)], ) # mock local server fakes successful authentication by immediately returning a well-formed response oauth_state = "state" auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock(wait_for_redirect=lambda: auth_code_response)) with patch(InteractiveBrowserCredential.__module__ + ".uuid.uuid4", lambda: oauth_state): with patch(WEBBROWSER_OPEN, lambda _: True): credential = InteractiveBrowserCredential( _cache=TokenCache(), authority=environment, client_id=client_id, server_class=server_class, tenant_id=tenant_id, transport=transport, ) record = credential._authenticate(scopes=(scope,)) assert record.authority == environment assert record.home_account_id == object_id + "." + home_tenant assert record.tenant_id == home_tenant assert record.username == username # credential should have a cached access token for the scope used in authenticate with patch(WEBBROWSER_OPEN, Mock(side_effect=Exception("credential should authenticate silently"))): token = credential.get_token(scope) assert token.token == access_token
def test_browser_credential(): transport = Mock(wraps=RequestsTransport()) credential = InteractiveBrowserCredential(transport=transport) scope = "https://management.azure.com/.default" # N.B. this is valid only in Public Cloud record = credential.authenticate(scopes=(scope, )) assert record.authority assert record.home_account_id assert record.tenant_id assert record.username # credential should have a cached access token for the scope used in authenticate with patch( WEBBROWSER_OPEN, Mock(side_effect=Exception( "credential should authenticate silently"))): token = credential.get_token(scope) assert token.token credential = InteractiveBrowserCredential(transport=transport) token = credential.get_token(scope) assert token.token with patch( WEBBROWSER_OPEN, Mock(side_effect=Exception( "credential should authenticate silently"))): second_token = credential.get_token(scope) assert second_token.token == token.token # every request should have the correct User-Agent for call in transport.send.call_args_list: args, _ = call request = args[0] assert request.headers["User-Agent"] == USER_AGENT
def test_redirect_uri(): """The credential should configure the redirect server to use a given redirect_uri""" expected_hostname = "localhost" expected_port = 42424 expected_message = "test_redirect_uri" server = Mock(side_effect=Exception(expected_message)) # exception prevents this test actually authenticating credential = InteractiveBrowserCredential( redirect_uri="htps://{}:{}".format(expected_hostname, expected_port), _server_class=server ) with pytest.raises(ClientAuthenticationError) as ex: credential.get_token("scope") assert expected_message in ex.value.message server.assert_called_once_with(expected_hostname, expected_port, timeout=ANY)
def test_no_browser(): discovery_response = mock_response( json_payload={ name: "https://foo/bar" for name in ("authorization_endpoint", "token_endpoint", "tenant_discovery_endpoint") }) transport = validating_transport( requests=[Request()] * 2, responses=[discovery_response, discovery_response]) credential = InteractiveBrowserCredential(client_id="client-id", client_secret="secret", server_class=Mock(), transport=transport) with pytest.raises(ClientAuthenticationError, match=r".*browser.*"): credential.get_token("scope")
def test_wsl_fallback(uname, is_wsl): """the credential should invoke powershell.exe to open a browser in WSL when webbrowser.open fails""" auth_uri = "http://localhost" expected_access_token = "**" msal_acquire_token_result = dict( build_aad_response(access_token=expected_access_token, id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) msal_app = Mock( initiate_auth_code_flow=Mock(return_value={"auth_uri": auth_uri}), acquire_token_by_auth_code_flow=Mock( return_value=msal_acquire_token_result), ) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = InteractiveBrowserCredential(_server_class=Mock(), transport=transport) with patch(InteractiveBrowserCredential.__module__ + ".subprocess.call") as subprocess_call: subprocess_call.return_value = 0 with patch(InteractiveBrowserCredential.__module__ + ".platform.uname", lambda: uname): with patch.object(InteractiveBrowserCredential, "_get_app", lambda _: msal_app): with patch(WEBBROWSER_OPEN, lambda _: False): try: token = credential.get_token("scope") except CredentialUnavailableError: assert not is_wsl, "credential should invoke powershell.exe in WSL" return assert is_wsl, "credential should raise CredentialUnavailableError when not in WSL" assert token.token == expected_access_token assert subprocess_call.call_count == 1 args, kwargs = subprocess_call.call_args assert args[0][0] == "powershell.exe" assert auth_uri in args[0][-1] if platform.python_version() >= "3.3": assert "timeout" in kwargs
def _default_chained_credentials(self) -> ChainedTokenCredential: managed_identity = ManagedIdentityCredential() azure_cli = AzureCliCredential() environment = EnvironmentCredential() shared_token_cache = SharedTokenCacheCredential() interactive_browser = InteractiveBrowserCredential() return ChainedTokenCredential(managed_identity, azure_cli, environment, shared_token_cache, interactive_browser)
def _credential( ) -> "InteractiveBrowserCredential": # Python 3.7+ can have forward reference if not AZURE_CLI_INSTALLED: raise RuntimeError( "In order to use webviz deploy features, you need to first install " "the optional deploy dependencies. You can do this by e.g. running " "'pip install webviz-config[deployment]'") return InteractiveBrowserCredential()
def test_claims_challenge(): """get_token and authenticate should pass any claims challenge to MSAL token acquisition APIs""" expected_claims = '{"access_token": {"essential": "true"}' auth_code_response = {"code": "authorization-code", "state": ["..."]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) msal_acquire_token_result = dict( build_aad_response(access_token="**", id_token=build_id_token()), id_token_claims=id_token_claims("issuer", "subject", "audience", upn="upn"), ) transport = Mock(send=Mock(side_effect=Exception( "this test mocks MSAL, so no request should be sent"))) credential = InteractiveBrowserCredential(_server_class=server_class, transport=transport) with patch.object(InteractiveBrowserCredential, "_get_app") as get_mock_app: msal_app = get_mock_app() msal_app.initiate_auth_code_flow.return_value = { "auth_uri": "http://localhost" } msal_app.acquire_token_by_auth_code_flow.return_value = msal_acquire_token_result with patch(WEBBROWSER_OPEN, lambda _: True): credential.authenticate(scopes=["scope"], claims=expected_claims) assert msal_app.acquire_token_by_auth_code_flow.call_count == 1 args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args assert kwargs["claims_challenge"] == expected_claims with patch(WEBBROWSER_OPEN, lambda _: True): credential.get_token("scope", claims=expected_claims) assert msal_app.acquire_token_by_auth_code_flow.call_count == 2 args, kwargs = msal_app.acquire_token_by_auth_code_flow.call_args assert kwargs["claims_challenge"] == expected_claims msal_app.get_accounts.return_value = [{ "home_account_id": credential._auth_record.home_account_id }] msal_app.acquire_token_silent_with_error.return_value = msal_acquire_token_result 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_interactive_credential(): oauth_state = "state" expected_token = "access-token" transport = validating_transport( requests=[Request()] * 2, # not validating requests because they're formed by MSAL responses=[ # expecting tenant discovery then a token request mock_response( json_payload={ "authorization_endpoint": "https://a/b", "token_endpoint": "https://a/b" }), mock_response( json_payload={ "access_token": expected_token, "expires_in": 42, "token_type": "Bearer", "ext_expires_in": 42, }), ], ) # mock local server fakes successful authentication by immediately returning a well-formed response 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="guid", client_secret="secret", server_class=server_class, transport=transport, instance_discovery= False, # kwargs are passed to MSAL; this one prevents an AAD verification request ) # ensure the request beginning the flow has a known state value with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state): token = credential.get_token("scope") assert token.token == expected_token
def _initialize_credentials(self): if self.subscription_id is not None \ and self.arm_base_url is not None: if self.vscode_tenant_id is None: self.vscode_tenant_id = self._get_tenant_id( arm_base_url=self.arm_base_url, subscription_id=self.subscription_id) if self.shared_cache_tenant_id is None: self.shared_cache_tenant_id = self._get_tenant_id( arm_base_url=self.arm_base_url, subscription_id=self.subscription_id) if self.interactive_browser_tenant_id is None: self.interactive_browser_tenant_id = self._get_tenant_id( arm_base_url=self.arm_base_url, subscription_id=self.subscription_id) credentials = [] # type: List[AsyncTokenCredential] if not self.exclude_token_file_credential: credentials.append(_TokenFileCredential()) if not self.exclude_environment_credential: credentials.append(EnvironmentCredential(authority=self.authority)) if not self.exclude_managed_identity_credential: credentials.append( ManagedIdentityCredential( client_id=self.managed_identity_client_id)) if not self.exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported( ): try: # username and/or tenant_id are only required when the cache contains tokens for multiple identities shared_cache = SharedTokenCacheCredential( username=self.shared_cache_username, tenant_id=self.shared_cache_tenant_id, authority=self.authority) credentials.append(shared_cache) except Exception as ex: # pylint:disable=broad-except _LOGGER.info("Shared token cache is unavailable: '%s'", ex) if not self.exclude_visual_studio_code_credential: credentials.append( VisualStudioCodeCredential(tenant_id=self.vscode_tenant_id)) if not self.exclude_cli_credential: credentials.append(AzureCliCredential()) if not self.exclude_powershell_credential: credentials.append(AzurePowerShellCredential()) if not self.exclude_interactive_browser_credential: credentials.append( InteractiveBrowserCredential( tenant_id=self.interactive_browser_tenant_id)) if not self.exclude_device_code_credential: credentials.append( DeviceCodeCredential( tenant_id=self.interactive_browser_tenant_id)) self.credentials = credentials
def test_token_cache_persistence_options(): with mock.patch("azure.identity._persistent_cache.msal_extensions"): # [START snippet] cache_options = TokenCachePersistenceOptions() credential = InteractiveBrowserCredential( cache_persistence_options=cache_options) # specify a cache name to isolate the cache from other applications TokenCachePersistenceOptions(name="my_application") # configure the cache to fall back to unencrypted storage when encryption isn't available TokenCachePersistenceOptions(allow_unencrypted_storage=True)
def _get_secret_client(self): if self.authn_type == "device": authority = self.authority_uri.replace("https://", "") credentials = DeviceCodeCredential( client_id=self.settings.CLIENT_ID, authority=authority, prompt_callback=_device_code_callback, ) else: credentials = InteractiveBrowserCredential() # Create a secret client secret_client = SecretClient(self.vault_uri, credentials) return secret_client
def create_from_env( cls, account_name: str, file_system_name: str, interactive: bool = False, adl_secret: Optional[ADLSecret] = None, **kwargs, ) -> "ADLGen2FileSystem": """ Creates ADL Gen2 file system client. Parameters ---------- account_name: str Azure account name file_system_name: str Container name interactive: bool If true then use interactive authentication adl_secret: ADLSecret Azure authentication information Returns ------- ADLGen2FileSystem """ if interactive: logger.info("Attempting to use interactive azure authentication") credential = InteractiveBrowserCredential() else: if type(adl_secret) is not ADLSecret: raise ConfigException("Unsupported type for adl_secret '%s'" % type(adl_secret)) adl_secret = cast(ADLSecret, adl_secret) logger.info("Attempting to use datalake service authentication") credential = ClientSecretCredential( tenant_id=adl_secret.tenant_id, client_id=adl_secret.client_id, client_secret=adl_secret.client_secret, ) return cls.create_from_credential(account_name, file_system_name, credential, **kwargs)
import os import sys from azure.identity import AuthenticationRequiredError, InteractiveBrowserCredential from azure.keyvault.secrets import SecretClient # This sample uses Key Vault only for demonstration. Any client accepting azure-identity credentials will work the same. VAULT_URL = os.environ.get("VAULT_URL") if not VAULT_URL: print( "This sample expects environment variable 'VAULT_URL' to be set with the URL of a Key Vault." ) sys.exit(1) # If it's important for your application to prompt for authentication only at certain times, # create the credential with disable_automatic_authentication=True. This configures the credential to raise # when interactive authentication is required, instead of immediately beginning that authentication. credential = InteractiveBrowserCredential( disable_automatic_authentication=True) client = SecretClient(VAULT_URL, credential) try: secret_names = [s.name for s in client.list_properties_of_secrets()] except AuthenticationRequiredError as ex: # Interactive authentication is necessary to authorize the client's request. The exception carries the # requested authentication scopes. If you pass these to 'authenticate', it will cache an access token # for those scopes. credential.authenticate(scopes=ex.scopes) # the client operation should now succeed secret_names = [s.name for s in client.list_properties_of_secrets()]
def test_interactive_credential(mock_open): mock_open.side_effect = _validate_auth_request_url oauth_state = "state" client_id = "client-id" expected_refresh_token = "refresh-token" expected_token = "access-token" expires_in = 3600 authority = "authority" tenant_id = "tenant_id" endpoint = "https://{}/{}".format(authority, tenant_id) discovery_response = mock_response( json_payload={ name: endpoint for name in ("authorization_endpoint", "token_endpoint", "tenant_discovery_endpoint") }) transport = validating_transport( requests=[Request(url_substring=endpoint)] * 3 + [ Request(authority=authority, url_substring=endpoint, required_data={"refresh_token": expected_refresh_token}) ], responses=[ discovery_response, # instance discovery discovery_response, # tenant discovery mock_response(json_payload=build_aad_response( access_token=expected_token, expires_in=expires_in, refresh_token=expected_refresh_token, uid="uid", utid="utid", token_type="Bearer", )), mock_response( json_payload=build_aad_response(access_token=expected_token, expires_in=expires_in, token_type="Bearer")), ], ) # mock local server fakes successful authentication by immediately returning a well-formed response auth_code_response = {"code": "authorization-code", "state": [oauth_state]} server_class = Mock(return_value=Mock( wait_for_redirect=lambda: auth_code_response)) credential = InteractiveBrowserCredential( authority=authority, tenant_id=tenant_id, client_id=client_id, client_secret="secret", server_class=server_class, transport=transport, instance_discovery=False, validate_authority=False, ) # The credential's auth code request includes a uuid which must be included in the redirect. Patching to # set the uuid requires less code here than a proper mock server. with patch("azure.identity._credentials.browser.uuid.uuid4", lambda: oauth_state): token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # token should be cached, get_token shouldn't prompt again token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # As of MSAL 1.0.0, applications build a new client every time they redeem a refresh token. # Here we patch the private method they use for the sake of test coverage. # TODO: this will probably break when this MSAL behavior changes app = credential._get_app() app._build_client = lambda *_: app.client # pylint:disable=protected-access now = time.time() # expired access token -> credential should use refresh token instead of prompting again with patch("time.time", lambda: now + expires_in): token = credential.get_token("scope") assert token.token == expected_token assert mock_open.call_count == 1 # ensure all expected requests were sent assert transport.send.call_count == 4
def test_no_scopes(): """The credential should raise when get_token is called with no scopes""" with pytest.raises(ValueError): InteractiveBrowserCredential().get_token()