diff --git a/ad-connector/pom.xml b/ad-connector/pom.xml
index 36286b000bc6dccd262b3c7affcc96ce6ae6743e..7c551eb83c66ed718d6c02af4df0a5c6aeff74ce 100644
--- a/ad-connector/pom.xml
+++ b/ad-connector/pom.xml
@@ -27,6 +27,12 @@
             <groupId>com.imperva.ddc</groupId>
             <version>${imperva.ddc.version}</version>
         </dependency>
+        <dependency>
+            <groupId>net.jami</groupId>
+            <artifactId>jams-server</artifactId>
+            <version>2.0</version>
+            <scope>compile</scope>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java
index 80d93744d8e335c7e6d8e072ef4d6937dc03e98b..7511476c4a0508149df3cc3c973ed1850bc33aab 100644
--- a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java
+++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java
@@ -37,6 +37,7 @@ import net.jami.jams.common.objects.user.UserProfile;
 import net.jami.jams.common.serialization.JsoniterRegistry;
 
 import java.util.List;
+import java.util.Optional;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 @Slf4j
@@ -93,13 +94,13 @@ public class ADConnector implements AuthenticationSource {
     }
 
     @Override
-    public List<UserProfile> searchUserProfiles(String queryString, String field) {
-        return userProfileService.getUserProfile(queryString, field,false);
+    public List<UserProfile> searchUserProfiles(String queryString, String field, Optional<Integer> page) {
+        return userProfileService.getUserProfile(queryString, field, false, page);
     }
 
     @Override
     public UserProfile getUserProfile(String username) {
-        List<UserProfile> result = userProfileService.getUserProfile(username, "LOGON_NAME",true);
+        List<UserProfile> result = userProfileService.getUserProfile(username, "LOGON_NAME",true, Optional.empty());
         if(result == null || result.size() != 1) return null;
         return result.get(0);
     }
diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java
index f2080882668fe0b60014fe1eef6e5cd3d72f3cdb..9a99d5aa9ee2a6b595a51384ed93b97dee250df0 100644
--- a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java
+++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java
@@ -38,18 +38,22 @@ import com.imperva.ddc.core.query.QueryRequest;
 import com.imperva.ddc.core.query.QueryResponse;
 import lombok.extern.slf4j.Slf4j;
 import net.jami.jams.ad.connector.ADConnector;
+import net.jami.jams.common.dao.StatementElement;
+import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.user.UserProfile;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Objects;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
+import static net.jami.jams.server.Server.dataStore;
+
 @Slf4j
 public class UserProfileService {
 
-    public List<UserProfile> getUserProfile(String queryString, String field,boolean exactMatch) {
+    private static final ConcurrentHashMap<String, String> fieldMap = ADConnector.settings.getFieldMappings();
+
+    public List<UserProfile> getUserProfile(String queryString, String field, boolean exactMatch, Optional<Integer> page) {
         Endpoint endpoint = ADConnector.getConnection();
         try {
             QueryRequest queryRequest = buildRequest(endpoint);
@@ -79,8 +83,32 @@ public class UserProfileService {
                 queryResponse = connector.execute();
             }
             List<List<Field>> results = queryResponse.getAll().stream().map(EntityResponse::getValue).collect(Collectors.toList());
-            return results.stream().map(UserProfileService::profileFromResponse).collect(Collectors.toList())
-                .stream().filter(Objects::nonNull).collect(Collectors.toList());
+
+            dataStore.NUM_PAGES = (Integer) results.size() / dataStore.RESULTS_PER_PAGE;
+            if (results.size() % dataStore.RESULTS_PER_PAGE != 0)
+                dataStore.NUM_PAGES++;
+
+            if (page.isPresent() && !results.isEmpty()) {
+                if (results.size() < dataStore.RESULTS_PER_PAGE)
+                    results = results.subList(0, results.size());
+                else if (page.get() * dataStore.RESULTS_PER_PAGE > results.size())
+                    results = results.subList((page.get()-1) * dataStore.RESULTS_PER_PAGE, results.size());
+                else
+                    results = results.subList((page.get()-1) * dataStore.RESULTS_PER_PAGE, (page.get() * dataStore.RESULTS_PER_PAGE));
+            }
+
+            if (results.size() == 0) return new ArrayList<>();
+            List<UserProfile> profilesFromResponse = results.stream().map(UserProfileService::profileFromResponse).collect(Collectors.toList());
+            for (UserProfile p: profilesFromResponse) {
+                StatementList statementList = new StatementList();
+                StatementElement st = new StatementElement("username", "=", p.getUsername(), "");
+                statementList.addStatement(st);
+
+                if (dataStore.getUserProfileDao().getObjects(statementList).isEmpty())
+                    dataStore.getUserProfileDao().storeObject(p);
+            }
+
+            return profilesFromResponse;
         } catch (Exception e) {
             log.error("Could not find entity with specified parameters.");
             return null;
@@ -90,7 +118,6 @@ public class UserProfileService {
     }
 
     public static QueryRequest buildRequest(Endpoint endpoint) {
-        HashMap<String, String> fieldMap = ADConnector.settings.getFieldMappings();
         QueryRequest queryRequest = new QueryRequest();
         queryRequest.setDirectoryType(DirectoryType.MS_ACTIVE_DIRECTORY);
         queryRequest.setEndpoints(new ArrayList<>() {{
@@ -106,8 +133,19 @@ public class UserProfileService {
     }
 
     public static UserProfile profileFromResponse(List<Field> fields) {
-        //Use reflection to remap.
-        HashMap<String, String> fieldMap = ADConnector.settings.getFieldMappings();
+        fieldMap.forEach((k, v) -> {
+            String temp = v;
+            fieldMap.put(k.toLowerCase(), temp);
+        });
+        fieldMap.forEach((k, v) -> {
+            char[] charArray = k.toCharArray();
+            for(int i=0; i < charArray.length; i++) {
+                if(Character.isUpperCase( charArray[i] )) {
+                    fieldMap.remove(k);
+                    break;
+                }
+            }
+        });
 
         try {
             UserProfile userProfile = new UserProfile();
diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
index 03cc491dfcb1a77ece20f04709a849235173e2ca..b1f088f3bb57f751e18594642120bc21888481dd 100644
--- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java
+++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
@@ -45,6 +45,7 @@ import org.flywaydb.core.Flyway;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 @Getter
 @Setter
@@ -60,6 +61,8 @@ public class DataStore implements AuthenticationSource {
     private JwtDao jwtDao;
     private UserProfileDao userProfileDao;
     private UserGroupMappingsDao userGroupMappingsDao;
+    public static final Integer RESULTS_PER_PAGE = 24;
+    public static Integer NUM_PAGES;
 
     //Implicitly connect to derby.
     public DataStore(String connectionString) {
@@ -90,7 +93,7 @@ public class DataStore implements AuthenticationSource {
         return userDao.storeObject(user);
     }
     @Override
-    public List<UserProfile> searchUserProfiles(String queryString, String field) {
+    public List<UserProfile> searchUserProfiles(String queryString, String field, Optional<Integer> page) {
         List<UserProfile> userList;
 
         if (!queryString.equals("*")) {
@@ -110,6 +113,19 @@ public class DataStore implements AuthenticationSource {
         if (userList == null)
             userList = new ArrayList<>();
 
+        NUM_PAGES = (Integer) userList.size() / RESULTS_PER_PAGE;
+        if (userList.size() % RESULTS_PER_PAGE != 0)
+            NUM_PAGES++;
+
+        if (page.isPresent() && !userList.isEmpty()) {
+            if (userList.size() < RESULTS_PER_PAGE)
+                userList = userList.subList(0, userList.size());
+            else if (page.get() * RESULTS_PER_PAGE > userList.size())
+                userList = userList.subList((page.get()-1) * RESULTS_PER_PAGE, userList.size());
+            else
+                userList = userList.subList((page.get()-1) * RESULTS_PER_PAGE, (page.get() * RESULTS_PER_PAGE));
+        }
+
         return userList;
     }
 
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java
index b65fb69544a79304b952ecd81c1bcc88968388d3..bf01d7edd58b8e7759dfc4768589e9b8a0d79e16 100644
--- a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java
@@ -26,11 +26,12 @@ import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.objects.user.UserProfile;
 
 import java.util.List;
+import java.util.Optional;
 
 public interface AuthenticationSource {
 
     boolean createUser(User user);
-    List<UserProfile> searchUserProfiles(String queryString, String field);
+    List<UserProfile> searchUserProfiles(String queryString, String field, Optional<Integer> page);
     UserProfile getUserProfile(String username);
     boolean setUserProfile(UserProfile userProfile);
     boolean authenticate(String username, String password);
diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java
index 7612bee7b1263b19eca032594b30e4d938c5af7c..6eb375211d0e3cf4d6853bed1c19d0b19c05760d 100644
--- a/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java
+++ b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java
@@ -26,6 +26,7 @@ import lombok.Getter;
 import lombok.Setter;
 
 import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Getter
 @Setter
@@ -37,6 +38,6 @@ public class ActiveDirectorySettings {
     private String  host;
     private String  username;
     private String  password;
-    private HashMap<String,String> fieldMappings;
+    private ConcurrentHashMap<String,String> fieldMappings;
 
 }
diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/customInputStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/customInputStyle.js
index ea3165df7772095a66fa381eb97f799c2c244f2e..d593f7afbf8a0bfe0e2d15cc418c86106f82daa6 100644
--- a/jams-react-client/src/assets/jss/material-dashboard-react/components/customInputStyle.js
+++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/customInputStyle.js
@@ -3,33 +3,32 @@ import {
   dangerColor,
   successColor,
   grayColor,
-  defaultFont
+  defaultFont,
 } from "assets/jss/material-dashboard-react.js";
-
 const customInputStyle = {
   disabled: {
     "&:before": {
-      backgroundColor: "transparent !important"
-    }
+      backgroundColor: "transparent !important",
+    },
   },
   underline: {
     "&:hover:not($disabled):before,&:before": {
       borderColor: grayColor[4] + " !important",
-      borderWidth: "1px !important"
+      borderWidth: "1px !important",
     },
     "&:after": {
-      borderColor: primaryColor[0]
-    }
+      borderColor: primaryColor[0],
+    },
   },
   underlineError: {
     "&:after": {
-      borderColor: dangerColor[0]
-    }
+      borderColor: dangerColor[0],
+    },
   },
   underlineSuccess: {
     "&:after": {
-      borderColor: successColor[0]
-    }
+      borderColor: successColor[0],
+    },
   },
   labelRoot: {
     ...defaultFont,
@@ -37,13 +36,13 @@ const customInputStyle = {
     fontWeight: "400",
     fontSize: "14px",
     lineHeight: "1.42857",
-    letterSpacing: "unset"
+    letterSpacing: "unset",
   },
   labelRootError: {
-    color: dangerColor[0]
+    color: dangerColor[0],
   },
   labelRootSuccess: {
-    color: successColor[0]
+    color: successColor[0],
   },
   feedback: {
     position: "absolute",
@@ -54,17 +53,13 @@ const customInputStyle = {
     width: "24px",
     height: "24px",
     textAlign: "center",
-    pointerEvents: "none"
-  },
-  marginTop: {
-    marginTop: "16px"
+    pointerEvents: "none",
   },
   formControl: {
     paddingBottom: "10px",
-    margin: "27px 0 0 0",
+    // margin: "27px 0 0 0",
     position: "relative",
-    verticalAlign: "unset"
-  }
+    verticalAlign: "unset",
+  },
 };
-
 export default customInputStyle;
diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js
index 800d6961e5e4d2f18afa2a2564b2880c16e43c6c..9c6e7d3a08fb5295273b08d67746816c2585c8a4 100644
--- a/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js
+++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/headerStyle.js
@@ -22,6 +22,9 @@ const headerStyle = (theme) => ({
     [theme.breakpoints.down("md")]: {
       width: "130px",
     },
+    [theme.breakpoints.up("md")]: {
+      display: "none",
+    },
     paddingTop: "10px",
     zIndex: "1029",
     color: grayColor[7],
diff --git a/jams-react-client/src/components/Drawer/Drawer.js b/jams-react-client/src/components/Drawer/Drawer.js
index c5fff76e72beed9da02201cf4b7df75d84980a50..342d1d71a36808055bf168ccc4ae4d0b42a503be 100644
--- a/jams-react-client/src/components/Drawer/Drawer.js
+++ b/jams-react-client/src/components/Drawer/Drawer.js
@@ -86,12 +86,12 @@ export default function TemporaryDrawer(props) {
       configApiCall(
         api_path_get_user_directory_search,
         "GET",
-        { queryString: "*" },
+        { queryString: "*", page: "1" },
         null
       )
     )
       .then((response) => {
-        let resp = response.data;
+        let resp = response.data.profiles;
         if (!addingToGroup) {
           let contacts = resp.filter((data) => {
             if (data.username !== props.username) return data;
@@ -193,12 +193,18 @@ export default function TemporaryDrawer(props) {
       configApiCall(
         api_path_get_user_directory_search,
         "GET",
-        { queryString: value ? value : "*" },
+        { queryString: value ? value : "*", page: "1" },
         null
       )
     )
       .then((response) => {
-        setUsers(response.data);
+        let resp = response.data.profiles;
+        if (!addingToGroup) {
+          let contacts = resp.filter((data) => {
+            if (data.username !== props.username) return data;
+          });
+          setUsers(contacts);
+        } else setUsers(resp);
       })
       .catch((error) => {
         console.log(error);
diff --git a/jams-react-client/src/components/Grid/GridContainer.js b/jams-react-client/src/components/Grid/GridContainer.js
index 4c815694f6e2aedf356c93842ea11456a9d71f25..635a2a95de738eea239adc6314485a0db78d3a0a 100644
--- a/jams-react-client/src/components/Grid/GridContainer.js
+++ b/jams-react-client/src/components/Grid/GridContainer.js
@@ -7,9 +7,9 @@ import Grid from "@material-ui/core/Grid";
 
 const styles = {
   grid: {
-    margin: "0 -15px !important",
-    width: "unset"
-  }
+      // margin: "0 -15px !important",
+      width: "unset",
+  },
 };
 
 const useStyles = makeStyles(styles);
@@ -25,5 +25,5 @@ export default function GridContainer(props) {
 }
 
 GridContainer.propTypes = {
-  children: PropTypes.node
+  children: PropTypes.node,
 };
diff --git a/jams-react-client/src/views/Users/Users.js b/jams-react-client/src/views/Users/Users.js
index 42f567165fdeacab3dab0033ea5b031bea0367af..c85c8f43db3577eea8cfa7c6b7a21ebea8ae8099 100644
--- a/jams-react-client/src/views/Users/Users.js
+++ b/jams-react-client/src/views/Users/Users.js
@@ -4,6 +4,7 @@ import { Switch, Route, Redirect } from "react-router-dom";
 // @material-ui/core components
 import { makeStyles } from "@material-ui/core/styles";
 import InputLabel from "@material-ui/core/InputLabel";
+import Pagination from "@material-ui/lab/Pagination";
 // core components
 import GridItem from "components/Grid/GridItem.js";
 import GridContainer from "components/Grid/GridContainer.js";
@@ -16,33 +17,25 @@ import CardBody from "components/Card/CardBody.js";
 import CardFooter from "components/Card/CardFooter.js";
 import UserProfile from "views/UserProfile/UserProfile.js";
 import Divider from "@material-ui/core/Divider";
-
 import PersonIcon from "@material-ui/icons/Person";
 import PermIdentityIcon from "@material-ui/icons/PermIdentity";
 import PhoneOutlinedIcon from "@material-ui/icons/PhoneOutlined";
 import InfoIcon from "@material-ui/icons/Info";
 import BusinessOutlinedIcon from "@material-ui/icons/BusinessOutlined";
 import Search from "@material-ui/icons/Search";
-
 import MailOutlineIcon from "@material-ui/icons/MailOutline";
 import axios from "axios";
 import configApiCall from "api.js";
 import auth from "auth.js";
 import { api_path_get_user_directory_search } from "globalUrls";
-
 import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
 import KeyboardReturnIcon from "@material-ui/icons/KeyboardReturn";
-
 import jami from "assets/img/faces/jami.png";
 import noProfilePicture from "assets/img/faces/no-profile-picture.png";
 import EditCreateUserProfile from "views/UserProfile/EditCreateUserProfile";
-
 import LinearProgress from "@material-ui/core/LinearProgress";
-
 import headerLinksStyle from "assets/jss/material-dashboard-react/components/headerLinksStyle.js";
-
 import { debounce } from "lodash";
-
 const styles = {
   ...headerLinksStyle,
   cardCategoryWhite: {
@@ -76,19 +69,23 @@ const styles = {
     alignItems: "center",
   },
 };
-
 const useStyles = makeStyles(styles);
-
 export default function Users(props) {
   const classes = useStyles();
   const history = useHistory();
   const [users, setUsers] = React.useState([]);
   const [noUsersFound, setNoUsersFound] = React.useState(false);
+  const [noMatchFound, setNoMatchFound] = React.useState(false);
   const [selectedUsername, setSelectedUsername] = React.useState("");
   const [createUser, setCreateUser] = React.useState(false);
   const [loading, setLoading] = React.useState(false);
   const [progress, setProgress] = React.useState(0);
 
+  const [searchValue, setSearchValue] = React.useState("");
+
+  const [selectedPage, setSelectedPage] = React.useState(1);
+
+  const [numberPages, setNumberPages] = React.useState(1);
   useEffect(() => {
     setLoading(true);
     const timer = setInterval(() => {
@@ -104,13 +101,15 @@ export default function Users(props) {
       configApiCall(
         api_path_get_user_directory_search,
         "GET",
-        { queryString: "*" },
+        { queryString: "*", page: "1" },
         null
       )
     )
       .then((response) => {
         if (response.status != 204) {
-          setUsers(response.data);
+          setUsers(response.data.profiles);
+
+          setNumberPages(response.data.numPages);
         } else {
           setNoUsersFound(true);
         }
@@ -127,47 +126,66 @@ export default function Users(props) {
       clearInterval(timer);
     };
   }, []);
-
   const [selectedProfile, setSelectedProfile] = useState(false);
-
   const redirectToUserProfile = (e, username) => {
     e.preventDefault();
     setSelectedProfile(true);
     setSelectedUsername(username);
   };
-
-  const searchUsers = (value) => {
+  const searchUsers = (value, page = "1") => {
+    setSelectedPage(page);
+    setLoading(true);
+    setNoMatchFound(false);
+    setUsers([]);
+    const timer = setInterval(() => {
+      setProgress((oldProgress) => {
+        if (oldProgress === 100) {
+          return 0;
+        }
+        const diff = Math.random() * 10;
+        return Math.min(oldProgress + diff, 100);
+      });
+    }, 500);
     axios(
       configApiCall(
         api_path_get_user_directory_search,
         "GET",
-        { queryString: value ? value : "*" },
+        { queryString: value ? value : "*", page: page },
         null
       )
     )
       .then((response) => {
-        setUsers(response.data);
+        setLoading(false);
+        if (response.status != 204) {
+          setUsers(response.data.profiles);
+          setNumberPages(response.data.numPages);
+        } else {
+          setNoMatchFound(true);
+        }
       })
       .catch((error) => {
         console.log(error);
         setUsers([]);
+
+        setNoMatchFound(true);
         if (error.response.status == 401) {
           auth.authenticated = false;
           history.push("/");
         }
       });
   };
-
   const initSearchUsers = useCallback(
-    debounce((searchValue) => searchUsers(searchValue), 500),
+    debounce((value) => searchUsers(value), 500),
     []
   );
-
   const handleSearchUsers = (e) => {
-    const searchValue = e.target.value;
-    initSearchUsers(searchValue);
+    setSearchValue(e.target.value);
+    initSearchUsers(e.target.value);
   };
 
+  const handleChangePage = (e, page) => {
+    searchUsers(searchValue, page);
+  };
   if (!auth.hasAdminScope()) {
     return (
       <div>
@@ -205,37 +223,51 @@ export default function Users(props) {
                 <AddCircleOutlineIcon /> Create user
               </Button>
             )}
-            <div className={classes.searchWrapper}>
-              {!noUsersFound && (
-                <CustomInput
-                  formControlProps={{
-                    className: classes.margin + " " + classes.search,
-                  }}
-                  inputProps={{
-                    placeholder:
-                      "Search users using (username, name, phone, email, ...)",
-                    inputProps: {
-                      "aria-label": "Search users",
-                    },
-                    onKeyUp: handleSearchUsers,
-                  }}
-                />
-              )}
-              {!noUsersFound && <Search />}
-              <div className={classes.loading}>
-                {loading && (
-                  <LinearProgress variant="determinate" value={progress} />
+            <GridContainer>
+              <GridItem xs={12} sm={12} md={6}>
+                {!noUsersFound && (
+                  <CustomInput
+                    formControlProps={{
+                      className: classes.margin + " " + classes.search,
+                    }}
+                    inputProps={{
+                      placeholder:
+                        "Search users using (username, name, phone, email, ...)",
+                      inputProps: {
+                        "aria-label": "Search users",
+                      },
+                      onKeyUp: handleSearchUsers,
+                    }}
+                  />
                 )}
-              </div>
-              {noUsersFound && (
-                <div className={classes.usersNotFound}>
-                  <InfoIcon />
-                  <p style={{ marginLeft: "10px" }}>No users found</p>
-                </div>
+                {!noUsersFound && <Search />}
+              </GridItem>
+              <GridItem xs={12} sm={12} md={6}>
+                {!noUsersFound && (
+                  <Pagination
+                    count={numberPages}
+                    page={selectedPage}
+                    onChange={handleChangePage}
+                  />
+                )}
+              </GridItem>
+            </GridContainer>
+
+            <div className={classes.loading}>
+              {loading && (
+                <LinearProgress variant="determinate" value={progress} />
               )}
             </div>
+
+            {noUsersFound && (
+              <div className={classes.usersNotFound}>
+                <InfoIcon />
+
+                <p style={{ marginLeft: "10px" }}>No users found</p>
+              </div>
+            )}
           </GridItem>
-          {!noUsersFound &&
+          {(!noUsersFound || !noMatchFound) &&
             users
               .sort(function (a, b) {
                 if (a.username < b.username) {
@@ -271,7 +303,6 @@ export default function Users(props) {
                             alt="..."
                           />
                         </CardAvatar>
-
                         <h4 className={classes.cardTitle}>
                           {user.firstName != ""
                             ? user.firstName
@@ -303,6 +334,16 @@ export default function Users(props) {
                   </Card>
                 </GridItem>
               ))}
+          {noMatchFound && (
+            <div className={classes.usersNotFound}>
+              <InfoIcon />
+
+              <p style={{ marginLeft: "10px" }}>
+                No users found mathcing <strong>{searchValue}</strong> search
+                value!
+              </p>
+            </div>
+          )}
         </GridContainer>
       </div>
     );
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java
index ccdf15d46fb57f3ffb9b2c3a5494615ec9b632de..00f57cbf0f973b4aac83eee9bcdf0ead2d862ab7 100644
--- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/AddUserToGroupFlow.java
@@ -8,6 +8,7 @@ import net.jami.jams.common.objects.user.UserProfile;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import static net.jami.jams.server.Server.dataStore;
 import static net.jami.jams.server.Server.userAuthenticationModule;
@@ -18,7 +19,7 @@ public class AddUserToGroupFlow {
     public static void addUserToGroup(String groupName, String username) {
         userAuthenticationModule.getAuthSources().forEach((k, v) -> {
             UserProfile profile = v.getUserProfile(username);
-            List<UserProfile> groupProfiles = v.searchUserProfiles(username, "LOGON_NAME");
+            List<UserProfile> groupProfiles = v.searchUserProfiles(username, "LOGON_NAME", Optional.empty());
             if (!groupProfiles.isEmpty()) {
                 UserGroupMapping mapping = null;
                 StatementList statementList = new StatementList();
diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
index 324f1a6dd529de0ddcc7b3af9cb3009cc7782ebe..79461707e5ae7ab0da40d86ab999b176b887b56a 100644
--- a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
+++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java
@@ -36,6 +36,7 @@ import net.jami.jams.dht.DeviceReceiptGenerator;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import static net.jami.jams.server.Server.certificateAuthority;
 import static net.jami.jams.server.Server.dataStore;
@@ -45,14 +46,15 @@ import static net.jami.jams.server.Server.userAuthenticationModule;
 @Slf4j
 public class RegisterDeviceFlow {
 
-    public static DeviceRegistrationResponse registerDevice(String username, DeviceRegistrationRequest registrationRequest){
+    public static DeviceRegistrationResponse registerDevice(String username,
+            DeviceRegistrationRequest registrationRequest) {
         try {
             StatementList statementList = new StatementList();
             statementList.addStatement(new StatementElement("username", "=", username, ""));
             User user = dataStore.getUserDao().getObjects(statementList).get(0);
             UserProfile userProfile = userAuthenticationModule.getAuthSources()
-                    .get(new AuthModuleKey(user.getRealm(),user.getUserType()))
-                    .searchUserProfiles(username,"LOGON_NAME").get(0);
+                    .get(new AuthModuleKey(user.getRealm(), user.getUserType()))
+                    .searchUserProfiles(username, "LOGON_NAME", Optional.empty()).get(0);
 
             if (user == null) {
                 log.error("Tried to enroll a device, but could not find a user, this is impossible!");
@@ -62,7 +64,7 @@ public class RegisterDeviceFlow {
             device.setOwner(username);
             device.setDisplayName(registrationRequest.getDeviceName());
             device = certificateAuthority.getSignedCertificate(user, device);
-            if(device == null){
+            if (device == null) {
                 log.error("Could not succesfully create a device certificate!");
                 return null;
             }
@@ -83,11 +85,9 @@ public class RegisterDeviceFlow {
                     userProfile.setGroupMemberships(list);
             }
 
-
-
-            //Now we build this response out.
+            // Now we build this response out.
             DeviceRegistrationResponse response = new DeviceRegistrationResponse();
-            if(userProfile.getGroupMemberships() != null) {
+            if (userProfile.getGroupMemberships() != null) {
                 userProfile.getGroupMemberships().forEach(e -> {
                     if (!e.equals("")) {
                         StatementElement st = new StatementElement("name", "=", e, "");
@@ -96,7 +96,7 @@ public class RegisterDeviceFlow {
 
                         Group group = dataStore.getGroupDao().getObjects(statementList1).get(0);
                         String policyName = group.getBlueprint();
-                        if(group != null && policyName != null){
+                        if (group != null && policyName != null) {
                             StatementElement st2 = new StatementElement("name", "=", policyName, "");
                             StatementList statementList2 = new StatementList();
                             statementList2.addStatement((st2));
@@ -110,21 +110,22 @@ public class RegisterDeviceFlow {
                     }
                 });
             }
-            //We need to set the device receipt....
-            String[] devReceipt = DeviceReceiptGenerator.generateReceipt(user.getPrivateKey(), user.getCertificate().getPublicKey(),
-                    device.getCertificate().getPublicKey(), user.getEthAddress());
+            // We need to set the device receipt....
+            String[] devReceipt = DeviceReceiptGenerator.generateReceipt(user.getPrivateKey(),
+                    user.getCertificate().getPublicKey(), device.getCertificate().getPublicKey(), user.getEthAddress());
             response.setDeviceReceipt(devReceipt[0]);
             response.setReceiptSignature(devReceipt[1]);
-            response.setDisplayName(userProfile.getFirstName()+" "+userProfile.getLastName());
-            //We need to set
+            response.setDisplayName(userProfile.getFirstName() + " " + userProfile.getLastName());
+            // We need to set
             response.setNameServer(nameServer.getURI());
-            if(userProfile.getProfilePicture() != null) response.setUserPhoto(userProfile.getProfilePicture());
-            //Finally we set the certificate chain.
-            response.setCertificateChain(new X509Certificate[]{certificateAuthority.getCA(),user.getCertificate(),device.getCertificate()});
+            if (userProfile.getProfilePicture() != null)
+                response.setUserPhoto(userProfile.getProfilePicture());
+            // Finally we set the certificate chain.
+            response.setCertificateChain(new X509Certificate[] { certificateAuthority.getCA(), user.getCertificate(),
+                    device.getCertificate() });
             return response;
-        }
-        catch (Exception e){
-            log.error("An exception has occurred while trying to enroll a device with error {}",e.getMessage());
+        } catch (Exception e) {
+            log.error("An exception has occurred while trying to enroll a device with error {}", e.getMessage());
             return null;
         }
     }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java
index 735bb96aacbdb382a7d8def9747d6d97194f0594..33b5d5bdd92c3f304316e3e6efb4fe320b957c92 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/DirectoryEntryServlet.java
@@ -38,6 +38,7 @@ import net.jami.jams.common.objects.user.UserProfile;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Optional;
 
 import static net.jami.jams.server.Server.dataStore;
 import static net.jami.jams.server.Server.userAuthenticationModule;
@@ -202,7 +203,7 @@ public class DirectoryEntryServlet extends HttpServlet {
             User user = dataStore.getUserDao().getObjects(statementList).get(0);
             List<UserProfile> userProfiles = new ArrayList<>();
             userAuthenticationModule.getAuthSources().forEach((k, v) -> {
-                userProfiles.addAll(v.searchUserProfiles(user.getUsername(), "LOGON_NAME"));
+                userProfiles.addAll(v.searchUserProfiles(user.getUsername(), "LOGON_NAME", Optional.empty()));
             });
             if(req.getParameter("format") != null && req.getParameter("format").equals("vcard")){
                 resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes());
@@ -213,7 +214,7 @@ public class DirectoryEntryServlet extends HttpServlet {
         if (req.getParameter("directory") != null && req.getParameter("directoryType") != null) {
             List<UserProfile> profiles = userAuthenticationModule.getAuthSources()
                     .get(new AuthModuleKey(req.getParameter("directory"), AuthenticationSourceType.fromString(req.getParameter("directoryType"))))
-                    .searchUserProfiles(req.getParameter("username"), "LOGON_NAME");
+                    .searchUserProfiles(req.getParameter("username"), "LOGON_NAME", Optional.empty());
             if(req.getParameter("format") != null && req.getParameter("format").equals("vcard")){
                 resp.getOutputStream().write(profiles.get(0).getAsVCard().getBytes());
             }
@@ -222,7 +223,7 @@ public class DirectoryEntryServlet extends HttpServlet {
         }
         List<UserProfile> userProfiles = new ArrayList<>();
         userAuthenticationModule.getAuthSources().forEach((k, v) -> {
-            userProfiles.addAll(v.searchUserProfiles(req.getParameter("username"), "LOGON_NAME"));
+            userProfiles.addAll(v.searchUserProfiles(req.getParameter("username"), "LOGON_NAME", Optional.empty()));
         });
         if(req.getParameter("format") != null && req.getParameter("format").equals("vcard")){
             resp.getOutputStream().write(userProfiles.get(0).getAsVCard().getBytes());
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
index a8e25006c5c0e1ab2e9983c848daca388c2ab35f..2bdf886074eb56ac155720b56c50af4a3453b3fc 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
@@ -20,7 +20,6 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
  */
-
 package net.jami.jams.server.servlets.api.auth.directory;
 
 import com.jsoniter.output.JsonStream;
@@ -29,17 +28,21 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import net.jami.jams.common.annotations.JsonContent;
+import net.jami.jams.common.authentication.AuthenticationSource;
+import net.jami.jams.common.authentication.AuthenticationSourceType;
+import net.jami.jams.common.authmodule.AuthModuleKey;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.objects.user.UserGroupMapping;
 import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.server.servlets.api.install.CachedObjects;
+import org.json.JSONObject;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
 
 import static net.jami.jams.server.Server.dataStore;
 import static net.jami.jams.server.Server.nameServer;
@@ -47,10 +50,11 @@ import static net.jami.jams.server.Server.userAuthenticationModule;
 
 @WebServlet("/api/auth/directory/search")
 public class SearchDirectoryServlet extends HttpServlet {
-
-    //The search directory function does not automatically create users, this would be costly at this point
-    //right now, we will implement it when Jami supports lists of users. this is a work in progress as it
-    //requires changes on the name server as well.
+    // The search directory function does not automatically create users, this would
+    // be costly at this point
+    // right now, we will implement it when Jami supports lists of users. this is a
+    // work in progress as it
+    // requires changes on the name server as well.
     List<UserProfile> userProfiles = new ArrayList<>();
 
     /**
@@ -59,23 +63,42 @@ public class SearchDirectoryServlet extends HttpServlet {
     @Override
     @JsonContent
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
+        Optional<Integer> page;
+        if (req.getParameter("page") == null)
+            page = Optional.empty();
+        else
+            page = Optional.ofNullable(Integer.parseInt(req.getParameter("page")));
+
+        ConcurrentHashMap<AuthModuleKey, AuthenticationSource> authSources = new ConcurrentHashMap<>(
+                userAuthenticationModule.getAuthSources());
+
+        if (authSources.size() > 1) {
+            authSources.forEach((k, v) -> {
+                if (k.getType() == AuthenticationSourceType.LOCAL)
+                    authSources.remove(k);
+            });
+        }
 
-        userAuthenticationModule.getAuthSources().forEach((k, v) -> {
+        authSources.forEach((k, v) -> {
             if (req.getParameter("queryString").equals("*"))
-                userProfiles = v.searchUserProfiles(req.getParameter("queryString"), "FULL_TEXT_NAME");
+                userProfiles = v.searchUserProfiles(req.getParameter("queryString"), "FULL_TEXT_NAME", page);
             else {
-                userProfiles = v.searchUserProfiles(req.getParameter("queryString"), "FULL_TEXT_NAME");
-                List<UserProfile> profiles2 = v.searchUserProfiles(req.getParameter("queryString"), "LOGON_NAME");
-                for (Iterator<UserProfile> it = userProfiles.iterator(); it.hasNext(); ) {
-                    UserProfile p = it.next();
-                    for (UserProfile p2 : profiles2) {
-                        if (p2.equals(p))
-                            it.remove();
-                    }
+                userProfiles = v.searchUserProfiles(req.getParameter("queryString"), "FULL_TEXT_NAME", page);
+                if (userProfiles.isEmpty() && userProfiles
+                        .addAll(v.searchUserProfiles(req.getParameter("queryString"), "LOGON_NAME", page))) {
+                    Set<UserProfile> s = new TreeSet<UserProfile>(new Comparator<UserProfile>() {
+                        @Override
+                        public int compare(UserProfile o1, UserProfile o2) {
+                            if (o1.getUsername().equals(o2.getUsername()))
+                                return 0;
+                            return 1;
+                        }
+                    });
+                    s.addAll(userProfiles);
+                    userProfiles = new ArrayList<>(s);
                 }
-                userProfiles.addAll(profiles2);
-            }
 
+            }
             userProfiles.parallelStream().forEach(profile -> {
                 StatementList statementList = new StatementList();
                 StatementElement statementElement = new StatementElement("username", "=", profile.getUsername(), "");
@@ -96,9 +119,14 @@ public class SearchDirectoryServlet extends HttpServlet {
                 }
             });
         });
+
+        JSONObject obj = new JSONObject();
+        obj.put("profiles", userProfiles);
+        obj.put("numPages", dataStore.NUM_PAGES);
         if (!userProfiles.isEmpty()) {
-            resp.getOutputStream().write(JsonStream.serialize(userProfiles).getBytes());
+            resp.getOutputStream().write((obj.toString()).getBytes());
             resp.setStatus(200);
-        } else resp.setStatus(204, "No users were found!");
+        } else
+            resp.setStatus(204, "No users were found!");
     }
-}
+}
\ No newline at end of file
diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java
index 8179ed2192ceec1c03f2f500ce368dc48715e9b6..4e3902b903dde972d8e18bab944d1d2ed2d76090 100644
--- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java
+++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java
@@ -39,6 +39,7 @@ import org.ldaptive.Credential;
 import org.ldaptive.DefaultConnectionFactory;
 
 import java.util.List;
+import java.util.Optional;
 
 @Slf4j
 public class LDAPConnector implements AuthenticationSource {
@@ -69,13 +70,13 @@ public class LDAPConnector implements AuthenticationSource {
     }
 
     @Override
-    public List<UserProfile> searchUserProfiles(String queryString, String field) {
-        return userProfileService.getUserProfile(queryString,field,false);
+    public List<UserProfile> searchUserProfiles(String queryString, String field, Optional<Integer> page) {
+        return userProfileService.getUserProfile(queryString,field,false, page);
     }
 
     @Override
     public UserProfile getUserProfile(String username) {
-        List<UserProfile> results = userProfileService.getUserProfile(username,"LOGON_NAME",true);
+        List<UserProfile> results = userProfileService.getUserProfile(username,"LOGON_NAME",true, Optional.empty());
         if(results == null || results.size() != 1) return null;
         return results.get(0);
     }
@@ -98,7 +99,7 @@ public class LDAPConnector implements AuthenticationSource {
 
     @Override
     public boolean test() {
-        return (searchUserProfiles("*","LOGON_NAME").size() != 0);
+        return (searchUserProfiles("*","LOGON_NAME", Optional.empty()).size() != 0);
     }
 
 
diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java
index 29e026f063ca552d6fbe36f72a44be7fb93badc6..aeb8e5a1dcbd88d366f2dab15f264de001aded2d 100644
--- a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java
+++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java
@@ -34,9 +34,7 @@ import org.ldaptive.SearchOperation;
 import org.ldaptive.SearchRequest;
 import org.ldaptive.SearchResponse;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import static net.jami.jams.server.Server.dataStore;
@@ -49,7 +47,7 @@ public class UserProfileService {
         this.connectionFactory = connectionFactory;
     }
 
-    public List<UserProfile> getUserProfile(String queryString, String field, boolean exactMatch){
+    public List<UserProfile> getUserProfile(String queryString, String field, boolean exactMatch, Optional<Integer> page){
         Connection connection = null;
         try {
             queryString = queryString.replaceAll("[^\\x00-\\x7F]","*");
@@ -58,10 +56,24 @@ public class UserProfileService {
                 connection.open();
                 SearchOperation search = new SearchOperation(connectionFactory);
                 SearchResponse res = search.execute(buildRequest(queryString,field, exactMatch));
+
+                dataStore.NUM_PAGES = (Integer) res.getEntries().size() / dataStore.RESULTS_PER_PAGE;
+                if (res.getEntries().size() % dataStore.RESULTS_PER_PAGE != 0)
+                    dataStore.NUM_PAGES++;
+
+                if (page.isPresent() && !res.getEntries().isEmpty()) {
+                    if (res.getEntries().size() < dataStore.RESULTS_PER_PAGE)
+                        res= res.subResult(0, res.getEntries().size());
+                    else if (page.get() * dataStore.RESULTS_PER_PAGE > res.getEntries().size())
+                        res = res.subResult((page.get()-1) * dataStore.RESULTS_PER_PAGE, res.getEntries().size());
+                    else
+                        res = res.subResult((page.get()-1) * dataStore.RESULTS_PER_PAGE, (page.get() * dataStore.RESULTS_PER_PAGE));
+
+                }
+
                 if (res.getEntries().size() == 0) return new ArrayList<>();
                 List<UserProfile> profilesFromResponse = res.getEntries().stream().map(UserProfileService::profileFromResponse).collect(Collectors.toList());
                 for (UserProfile p: profilesFromResponse) {
-
                     StatementList statementList = new StatementList();
                     StatementElement st = new StatementElement("username", "=", p.getUsername(), "");
                     statementList.addStatement(st);
diff --git a/ldap-connector/src/test/java/tests/GenericLDAPTest.java b/ldap-connector/src/test/java/tests/GenericLDAPTest.java
index 82cb05e5146f6428835e6a427b8c8fd81663258f..cb5698460b136c38d586cc82fc4664eafc7b700a 100644
--- a/ldap-connector/src/test/java/tests/GenericLDAPTest.java
+++ b/ldap-connector/src/test/java/tests/GenericLDAPTest.java
@@ -11,6 +11,7 @@ import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder;
 
 import java.io.InputStream;
 import java.util.List;
+import java.util.Optional;
 
 public class GenericLDAPTest {
 
@@ -34,7 +35,7 @@ public class GenericLDAPTest {
     @Test
     public void testLookUp() throws Exception{
         initLdapConnector();
-        List<UserProfile> profiles = ldapConnector.searchUserProfiles("*","FULL_TEXT_NAME");
+        List<UserProfile> profiles = ldapConnector.searchUserProfiles("*","FULL_TEXT_NAME", Optional.empty());
         Assertions.assertEquals(2,profiles.size());
         Assertions.assertNotNull(profiles.get(0).getFirstName());
         Assertions.assertNotNull(profiles.get(1).getFirstName());
@@ -53,7 +54,7 @@ public class GenericLDAPTest {
     @Test
     public void getVcard() throws Exception{
         initLdapConnector();
-        List<UserProfile> profiles = ldapConnector.searchUserProfiles("Felix","FULL_TEXT_NAME");
+        List<UserProfile> profiles = ldapConnector.searchUserProfiles("Felix","FULL_TEXT_NAME", Optional.empty());
         Assert.assertEquals(1,profiles.size());
         Assert.assertNotNull(profiles.get(0).getUsername());
         String vcard = profiles.get(0).getAsVCard();