def test_no_scopes(): """The credential should raise ValueError when get_token is called with no scopes""" credential = AuthorizationCodeCredential("tenant-id", "client-id", "auth-code", "http://localhost") with pytest.raises(ValueError): credential.get_token()
def test_tenant_id(): transport = validating_transport( requests=[Request(required_headers={"User-Agent": USER_AGENT})], responses=[mock_response(json_payload=build_aad_response(access_token="**"))], ) credential = AuthorizationCodeCredential( "tenant-id", "client-id", "auth-code", "http://localhost", transport=transport ) credential.get_token("scope", tenant_id="tenant_id")
def test_policies_configurable(): policy = Mock(spec_set=SansIOHTTPPolicy, on_request=Mock()) def send(*_, **__): return mock_response(json_payload=build_aad_response(access_token="**")) credential = AuthorizationCodeCredential( "tenant-id", "client-id", "auth-code", "http://localhost", policies=[policy], transport=Mock(send=send) ) credential.get_token("scope") assert policy.on_request.called
def test_auth_code_credential(): client_id = "client id" tenant_id = "tenant" expected_code = "auth code" redirect_uri = "https://foo.bar" expected_token = AccessToken("token", 42) mock_client = Mock(spec=object) mock_client.obtain_token_by_authorization_code = Mock( return_value=expected_token) credential = AuthorizationCodeCredential( client_id=client_id, tenant_id=tenant_id, authorization_code=expected_code, redirect_uri=redirect_uri, client=mock_client, ) # first call should redeem the auth code token = credential.get_token("scope") assert token is expected_token assert mock_client.obtain_token_by_authorization_code.call_count == 1 _, kwargs = mock_client.obtain_token_by_authorization_code.call_args assert kwargs["code"] == expected_code # no auth code -> credential should return cached token mock_client.obtain_token_by_authorization_code = None # raise if credential calls this again mock_client.get_cached_access_token = lambda *_: expected_token token = credential.get_token("scope") assert token is expected_token # no auth code, no cached token -> credential should use refresh token mock_client.get_cached_access_token = lambda *_: None mock_client.get_cached_refresh_tokens = lambda *_: [ "this is a refresh token" ] mock_client.obtain_token_by_refresh_token = lambda *_, **__: expected_token token = credential.get_token("scope") assert token is expected_token
def test_multitenant_authentication_not_allowed(): expected_tenant = "expected-tenant" expected_token = "***" def send(request, **_): parsed = urlparse(request.url) tenant = parsed.path.split("/")[1] token = expected_token if tenant == expected_tenant else expected_token * 2 return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) credential = AuthorizationCodeCredential( expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send) ) token = credential.get_token("scope") assert token.token == expected_token token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token token = credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token * 2 with patch.dict("os.environ", {EnvironmentVariables.AZURE_IDENTITY_DISABLE_MULTITENANTAUTH: "true"}): token = credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token
def test_multitenant_authentication(): first_tenant = "first-tenant" first_token = "***" second_tenant = "second-tenant" second_token = first_token * 2 def send(request, **_): parsed = urlparse(request.url) tenant = parsed.path.split("/")[1] assert tenant in (first_tenant, second_tenant), 'unexpected tenant "{}"'.format(tenant) token = first_token if tenant == first_tenant else second_token return mock_response(json_payload=build_aad_response(access_token=token, refresh_token="**")) credential = AuthorizationCodeCredential( first_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send), ) token = credential.get_token("scope") assert token.token == first_token token = credential.get_token("scope", tenant_id=first_tenant) assert token.token == first_token token = credential.get_token("scope", tenant_id=second_tenant) assert token.token == second_token # should still default to the first tenant token = credential.get_token("scope") assert token.token == first_token
def test_multitenant_authentication_not_allowed(): """get_token(tenant_id=...) should raise when allow_multitenant_authentication is False (the default)""" expected_tenant = "expected-tenant" expected_token = "***" def send(request, **_): parsed = urlparse(request.url) tenant = parsed.path.split("/")[1] token = expected_token if tenant == expected_tenant else expected_token * 2 return mock_response(json_payload=build_aad_response( access_token=token, refresh_token="**")) credential = AuthorizationCodeCredential(expected_tenant, "client-id", "authcode", "https://localhost", transport=Mock(send=send)) token = credential.get_token("scope") assert token.token == expected_token # explicitly specifying the configured tenant is okay token = credential.get_token("scope", tenant_id=expected_tenant) assert token.token == expected_token # but any other tenant should get an error with pytest.raises(ClientAuthenticationError, match="allow_multitenant_authentication"): credential.get_token("scope", tenant_id="un" + expected_tenant) # ...unless the compat switch is enabled with patch.dict("os.environ", { EnvironmentVariables.AZURE_IDENTITY_ENABLE_LEGACY_TENANT_SELECTION: "true" }): token = credential.get_token("scope", tenant_id="un" + expected_tenant) assert token.token == expected_token, "credential should ignore tenant_id kwarg when the compat switch is enabled"
def test_auth_code_credential(): client_id = "client id" tenant_id = "tenant" expected_code = "auth code" redirect_uri = "https://localhost" expected_access_token = "access" expected_refresh_token = "refresh" expected_scope = "scope" auth_response = build_aad_response(access_token=expected_access_token, refresh_token=expected_refresh_token) transport = validating_transport( requests=[ Request( # first call should redeem the auth code url_substring=tenant_id, required_data={ "client_id": client_id, "code": expected_code, "grant_type": "authorization_code", "redirect_uri": redirect_uri, "scope": expected_scope, }, ), Request( # third call should redeem the refresh token url_substring=tenant_id, required_data={ "client_id": client_id, "grant_type": "refresh_token", "refresh_token": expected_refresh_token, "scope": expected_scope, }, ), ], responses=[mock_response(json_payload=auth_response)] * 2, ) cache = msal.TokenCache() credential = AuthorizationCodeCredential( client_id=client_id, tenant_id=tenant_id, authorization_code=expected_code, redirect_uri=redirect_uri, transport=transport, cache=cache, ) # first call should redeem the auth code token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 1 # no auth code -> credential should return cached token token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 1 # no auth code, no cached token -> credential should redeem refresh token cached_access_token = cache.find(cache.CredentialType.ACCESS_TOKEN)[0] cache.remove_at(cached_access_token) token = credential.get_token(expected_scope) assert token.token == expected_access_token assert transport.send.call_count == 2