From 778a41b9ef37db6969dd3588f4505da8ab1aa732 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 Jan 2025 16:14:24 +0100
Subject: [PATCH 1/3] [ResultsTableMUI] added styling on column headers and
 rows

---
 package.json                         |  1 +
 src/pages/results/ResultsTableMUI.js | 64 ++++++++++++++++++++++------
 2 files changed, 51 insertions(+), 14 deletions(-)

diff --git a/package.json b/package.json
index 893d7db..ee45802 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
     "@microlink/react-json-view": "^1.23.1",
     "@mui/icons-material": "^5.15.21",
     "@mui/material": "^5.15.21",
+    "@mui/styles": "^6.3.1",
     "downloadjs": "^1.4.7",
     "i18next": "^23.11.5",
     "i18next-http-backend": "^2.5.2",
diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js
index 1a7b22b..2e2dc71 100644
--- a/src/pages/results/ResultsTableMUI.js
+++ b/src/pages/results/ResultsTableMUI.js
@@ -2,24 +2,21 @@ import React, { useEffect, useMemo, useState } from 'react';
 import { Trans, useTranslation } from 'react-i18next';
 import MUIDataTable from 'mui-datatables';
 import { createTheme, ThemeProvider } from '@mui/material';
+import { makeStyles } from '@mui/styles';
 import { fetchPublicFields } from '../../actions/source';
 import { fetchUserFieldsDisplaySettings } from '../../actions/user';
 import { buildFieldName } from '../../Utils';
+import { useEuiTheme, transparentize, EuiTitle } from '@elastic/eui';
 
-const getMuiTheme = () =>
-  createTheme({
-    components: {
-      MuiTableRow: {
-        styleOverrides: {
-          root: {
-            '&:hover': {
-              cursor: 'pointer',
-            },
-          },
-        },
-      },
+const useStyles = makeStyles(() => ({
+  centeredHeader: {
+    '& span': {
+      display: 'flex',
+      justifyContent: 'center',
+      alignItems: 'center',
     },
-  });
+  },
+}));
 
 const ResultsTableMUI = ({
   searchResults,
@@ -30,6 +27,8 @@ const ResultsTableMUI = ({
   setResourceFlyoutDataFromId,
 }) => {
   const { t } = useTranslation('results');
+  const { euiTheme } = useEuiTheme();
+  const classes = useStyles();
   const [publicFields, setPublicFields] = useState([]);
   const [userFieldsIds, setUserFieldsIds] = useState([]);
   const [isLoading, setIsLoading] = useState(true);
@@ -64,12 +63,24 @@ const ResultsTableMUI = ({
         options: { display: 'excluded' },
       },
     ];
-    publicFields.forEach((publicField) => {
+    publicFields.forEach((publicField, index) => {
       dataColumns.push({
         name: publicField.field_name,
         label: buildFieldName(publicField.field_name),
         options: {
           display: userFieldsIds.includes(publicField.id),
+          // Apply styling on columns headers
+          setCellHeaderProps: () => ({
+            className: classes.centeredHeader,
+          }),
+          customHeadLabelRender: (columnMeta) => {
+            return (
+              <EuiTitle size={'xxs'}>
+                <p>{columnMeta.label}</p>
+              </EuiTitle>
+            );
+          },
+          // Truncate text to avoid oversize rows
           customBodyRenderLite: (dataIndex, rowIndex) => {
             let value = rows[rowIndex][publicField.field_name];
             if (value && value.length >= 150) {
@@ -77,6 +88,7 @@ const ResultsTableMUI = ({
             }
             return value;
           },
+          // Apply a max width on table cells
           setCellProps: () => ({
             style: {
               maxWidth: '350px',
@@ -193,6 +205,30 @@ const ResultsTableMUI = ({
     },
   };
 
+  const getMuiTheme = () =>
+    createTheme({
+      components: {
+        MUIDataTableBodyRow: {
+          styleOverrides: {
+            root: {
+              '&:nth-of-type(odd)': {
+                backgroundColor: transparentize(euiTheme.colors.lightShade, 0.5),
+              },
+            },
+          },
+        },
+        MuiTableRow: {
+          styleOverrides: {
+            root: {
+              '&:hover': {
+                cursor: 'pointer',
+              },
+            },
+          },
+        },
+      },
+    });
+
   const tableOptions = {
     print: false,
     download: false,
-- 
GitLab


From 3a3b9defc46fa98daf8e27e838769ce548d7a575 Mon Sep 17 00:00:00 2001
From: rbisson <remi.bisson@inrae.fr>
Date: Tue, 7 Jan 2025 16:15:13 +0100
Subject: [PATCH 2/3] [ResultsTableMUI] added comments

---
 src/pages/results/ResultsTableMUI.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js
index 2e2dc71..0fd0c86 100644
--- a/src/pages/results/ResultsTableMUI.js
+++ b/src/pages/results/ResultsTableMUI.js
@@ -63,7 +63,7 @@ const ResultsTableMUI = ({
         options: { display: 'excluded' },
       },
     ];
-    publicFields.forEach((publicField, index) => {
+    publicFields.forEach((publicField) => {
       dataColumns.push({
         name: publicField.field_name,
         label: buildFieldName(publicField.field_name),
@@ -73,6 +73,7 @@ const ResultsTableMUI = ({
           setCellHeaderProps: () => ({
             className: classes.centeredHeader,
           }),
+          // Apply styling on columns headers text
           customHeadLabelRender: (columnMeta) => {
             return (
               <EuiTitle size={'xxs'}>
-- 
GitLab


From 0559686b41bbc05054ced09666813d97f7ce21d6 Mon Sep 17 00:00:00 2001
From: Brett Choquet <brett.choquet@inra.fr>
Date: Fri, 17 Jan 2025 11:09:38 +0100
Subject: [PATCH 3/3] Feature/authentication

---
 .dockerignore                                 |  22 ++
 .gitlab-ci.yml                                |  16 ++
 Dockerfile                                    |  25 ++
 env.sh                                        | 121 ++++++++++
 nginx/gzip.conf                               |  44 ++++
 nginx/nginx.conf                              |  60 +++++
 package.json                                  |   7 +-
 public/env-config.js                          |   8 +
 public/index.html                             |  29 +--
 public/locales/en/profile.json                |   3 +-
 public/locales/fr/maps.json                   |   4 +-
 public/locales/fr/profile.json                |   3 +-
 src/App.js                                    |   4 +-
 src/Utils.js                                  | 208 ++---------------
 src/actions/index.js                          |   2 -
 src/actions/source.js                         |  61 -----
 src/actions/user.js                           | 216 ------------------
 src/components/Header/Header.js               |   2 +-
 src/components/Header/HeaderUserMenu.js       |   7 +-
 src/context/InSylvaGatekeeperClient.js        | 138 -----------
 src/context/InSylvaKeycloakClient.js          |  66 ------
 src/context/InSylvaSearchClient.js            |  67 ------
 src/context/InSylvaSourceManagerClient.js     |  34 ---
 src/context/UserContext.js                    | 141 ------------
 src/contexts/GatekeeperContext.js             |  19 ++
 src/contexts/TokenContext.js                  |  52 +++++
 src/i18n.js                                   |   2 +-
 src/index.js                                  |  58 ++---
 src/pages/profile/GroupSettings.js            | 166 +++++++-------
 src/pages/profile/MyProfile.js                |  13 +-
 src/pages/profile/Profile.js                  |  84 +++----
 src/pages/profile/RoleSettings.js             |  35 +--
 .../profile/UserFieldsDisplaySettings.js      |  49 ++--
 src/pages/results/ResultsTableMUI.js          | 140 +++++-------
 .../search/AdvancedSearch/AdvancedSearch.js   | 211 +++++++++--------
 src/pages/search/BasicSearch/BasicSearch.js   |  26 +--
 src/pages/search/Search.js                    |  93 +++++---
 src/services/GatekeeperService.js             | 148 ++++++++++++
 src/services/TokenService.js                  |  38 +++
 39 files changed, 1026 insertions(+), 1396 deletions(-)
 create mode 100644 .dockerignore
 create mode 100644 .gitlab-ci.yml
 create mode 100644 Dockerfile
 create mode 100755 env.sh
 create mode 100644 nginx/gzip.conf
 create mode 100644 nginx/nginx.conf
 create mode 100644 public/env-config.js
 delete mode 100644 src/actions/index.js
 delete mode 100644 src/actions/source.js
 delete mode 100644 src/actions/user.js
 delete mode 100644 src/context/InSylvaGatekeeperClient.js
 delete mode 100644 src/context/InSylvaKeycloakClient.js
 delete mode 100644 src/context/InSylvaSearchClient.js
 delete mode 100644 src/context/InSylvaSourceManagerClient.js
 delete mode 100644 src/context/UserContext.js
 create mode 100644 src/contexts/GatekeeperContext.js
 create mode 100644 src/contexts/TokenContext.js
 create mode 100644 src/services/GatekeeperService.js
 create mode 100644 src/services/TokenService.js

diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..7dc10ba
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,22 @@
+# Ignore node_modules directory
+node_modules/
+
+# Ignore npm debug log
+npm-debug.log
+
+# Ignore build artifacts
+dist/
+build/
+out/
+
+# Ignore development files
+*.log
+*.pid
+*.lock
+
+# Ignore version control files
+.git/
+.gitignore
+
+# Ignore certificates
+ssl/
\ No newline at end of file
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000..f0674db
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,16 @@
+stages:
+  - build
+
+build:
+  stage: build
+  image: docker:latest
+  services:
+    - docker:dind
+  script:
+    - apk add jq
+    - version=$(jq -r .version package.json)
+    - cat $ENV_PRODUCTION > .env.production
+    - docker build -t registry.forgemia.inra.fr/in-sylva-development/in-sylva.search.app:$version .
+    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
+    - docker push registry.forgemia.inra.fr/in-sylva-development/in-sylva.search.app:$version
+  when: manual
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..d2c2bcb
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,25 @@
+FROM node:18.20.5 as builder
+
+WORKDIR /app/
+COPY package.json .
+RUN yarn install
+COPY . .
+RUN yarn build
+
+FROM nginx:1.24-bullseye
+COPY --from=builder /app/build /usr/share/nginx/html
+
+RUN rm /etc/nginx/conf.d/default.conf
+COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
+COPY nginx/gzip.conf /etc/nginx/conf.d/gzip.conf
+
+WORKDIR /usr/share/nginx/html
+RUN chown -R :www-data /usr/share/nginx/html
+
+COPY ./env.sh .
+RUN chmod +x env.sh
+
+COPY .env.production .env
+RUN ./env.sh -e .env -o ./
+
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/env.sh b/env.sh
new file mode 100755
index 0000000..5c94083
--- /dev/null
+++ b/env.sh
@@ -0,0 +1,121 @@
+#!/bin/bash
+
+usage() {
+    BASENAME=$(basename "$0")
+    echo "Usage: $BASENAME -e <env-file> -o <output-file>"
+    echo "  -e, --env-file     Path to .env file"
+    echo "  -o, --output-file  Path to output '.env-config.js' file"
+    exit 1
+}
+
+while getopts "e:o:" opt; do
+    case $opt in
+    e)
+        ENV_FILE=$OPTARG
+        ;;
+    o)
+        OUT_DIRECTORY=$OPTARG
+        ;;
+    \?)
+        # Invalid option
+        echo "Invalid option: -$OPTARG"
+        usage
+        exit 1
+        ;;
+    :)
+        echo "Option -$OPTARG requires an argument."
+        usage
+        exit 1
+        ;;
+    esac
+done
+
+# Check if required options are provided
+if [ -z "$OUT_DIRECTORY" ]; then
+    echo "Error: -o (output directory) is required."
+    echo ""
+    usage
+    exit 1
+fi
+
+if [ -z "$ENV_FILE" ]; then
+    echo "Error: -e (environment file) is required."
+    echo ""
+    usage
+    exit 1
+fi
+
+ENV_FILE_PATH=$(realpath $ENV_FILE)
+
+# Check if the environment file exists
+if [[ ! -f $ENV_FILE_PATH ]]; then
+    echo "Environment file does not exist"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file is readable
+if [[ ! -r $ENV_FILE_PATH ]]; then
+    echo "Environment file is not readable"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file is empty
+if [[ ! -s $ENV_FILE_PATH ]]; then
+    echo "Environment file is empty"
+    echo ""
+    usage
+    exit 1
+fi
+
+# Check if the environment file has the correct format
+BAD_LINES=$(grep -v -P '^[^=\s]+=[^=\s]+$' "$ENV_FILE_PATH")
+if [ -n "$BAD_LINES" ]; then
+    echo "Environment file has incorrect format or contains empty values:"
+    echo "$BAD_LINES"
+    echo ""
+    usage
+    exit 1
+fi
+
+FULL_PATH_OUTPUT_DIRECTORY=$(realpath $OUT_DIRECTORY)
+
+# Check if the output directory is a directory, exists, and can be written
+if [[ ! -d $FULL_PATH_OUTPUT_DIRECTORY ]]; then
+    echo "Output directory does not exist or is not a directory"
+    echo ""
+    usage
+    exit 1
+fi
+
+if [[ ! -w $FULL_PATH_OUTPUT_DIRECTORY ]]; then
+    echo "Output directory is not writable"
+    echo ""
+    usage
+    exit 1
+fi
+
+OUTPUT_FILE="env-config.js"
+FULL_OUTPUT_PATH="$FULL_PATH_OUTPUT_DIRECTORY/$OUTPUT_FILE"
+
+# Remove the output file if it exists
+if [[ -f $FULL_OUTPUT_PATH ]]; then
+    rm $FULL_OUTPUT_PATH
+fi
+
+# Add assignment
+echo "window._env_ = {" >>$FULL_OUTPUT_PATH
+
+while read -r line || [[ -n "$line" ]]; do
+    # Split env variables by '='
+    varname=$(echo $line | cut -d'=' -f1)
+    value=$(echo $line | cut -d'=' -f2)
+
+    # Append configuration property to JS file
+    echo "  $varname: \"$value\"," >>$FULL_OUTPUT_PATH
+done <$ENV_FILE_PATH
+
+echo "}" >>$FULL_OUTPUT_PATH
diff --git a/nginx/gzip.conf b/nginx/gzip.conf
new file mode 100644
index 0000000..2f54ae1
--- /dev/null
+++ b/nginx/gzip.conf
@@ -0,0 +1,44 @@
+# Enable Gzip compressed.
+# gzip on;
+
+  # Enable compression both for HTTP/1.0 and HTTP/1.1 (required for CloudFront).
+  gzip_http_version  1.0;
+
+  # Compression level (1-9).
+  # 5 is a perfect compromise between size and cpu usage, offering about
+  # 75% reduction for most ascii files (almost identical to level 9).
+  gzip_comp_level    5;
+
+  # Don't compress anything that's already small and unlikely to shrink much
+  # if at all (the default is 20 bytes, which is bad as that usually leads to
+  # larger files after gzipping).
+  gzip_min_length    256;
+
+  # Compress data even for clients that are connecting to us via proxies,
+  # identified by the "Via" header (required for CloudFront).
+  gzip_proxied       any;
+
+  # Tell proxies to cache both the gzipped and regular version of a resource
+  # whenever the client's Accept-Encoding capabilities header varies;
+  # Avoids the issue where a non-gzip capable client (which is extremely rare
+  # today) would display gibberish if their proxy gave them the gzipped version.
+  gzip_vary          on;
+
+  # Compress all output labeled with one of the following MIME-types.
+  gzip_types
+    application/atom+xml
+    application/javascript
+    application/json
+    application/rss+xml
+    application/vnd.ms-fontobject
+    application/x-font-ttf
+    application/x-web-app-manifest+json
+    application/xhtml+xml
+    application/xml
+    font/opentype
+    image/svg+xml
+    image/x-icon
+    text/css
+    text/plain
+    text/x-component;
+  # text/html is always compressed by HttpGzipModule
\ No newline at end of file
diff --git a/nginx/nginx.conf b/nginx/nginx.conf
new file mode 100644
index 0000000..e3c6319
--- /dev/null
+++ b/nginx/nginx.conf
@@ -0,0 +1,60 @@
+include /etc/nginx/mime.types;
+
+server {
+
+    listen 80;
+    server_name -;
+
+    ssl_certificate /etc/ssl/fullchain.pem;
+    ssl_certificate_key /etc/ssl/fullchain.pem.key;
+
+    proxy_set_header Host $host;
+    proxy_set_header X-Real-IP $remote_addr;
+    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+    proxy_set_header X-Forwarded-Host $host;
+    proxy_set_header X-Forwarded-Server $host;
+    proxy_set_header X-Forwarded-Port $server_port;
+    proxy_set_header X-Forwarded-Proto $scheme;
+
+    access_log /var/log/nginx/host.access.log;
+    error_log /var/log/nginx/host.error.log;
+
+    root /usr/share/nginx/html;
+    index index.html index.htm;
+
+    location /static/media/ {
+        try_files $uri /usr/share/nginx/html/static/media;
+    }
+
+    location / {
+
+        root /usr/share/nginx/html;
+        index index.html;
+        autoindex on;
+        set $fallback_file /index.html;
+        if ($http_accept !~ text/html) {
+            set $fallback_file /null;
+        }
+        if ($uri ~ /$) {
+            set $fallback_file /null;
+        }
+        try_files $uri $fallback_file;
+
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection 'upgrade';
+        proxy_set_header Host $host;
+        proxy_cache_bypass $http_upgrade;
+
+        add_header 'Access-Control-Allow-Origin' "$http_origin" always;
+        add_header 'Access-Control-Allow-Credentials' 'true' always;
+        add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
+        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
+    }
+
+    error_page 500 502 503 504 /50x.html;
+
+    location = /50x.html {
+        root /usr/share/nginx/html;
+    }
+}
diff --git a/package.json b/package.json
index ee45802..299f394 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "in-sylva.search",
-  "version": "1.0.0",
+  "version": "1.0.1",
   "private": true,
   "homepage": ".",
   "dependencies": {
@@ -18,6 +18,7 @@
     "i18next-http-backend": "^2.5.2",
     "moment": "^2.27.0",
     "mui-datatables": "^4.3.0",
+    "oidc-react": "^3.4.1",
     "ol": "^9.2.4",
     "proj4": "^2.11.0",
     "react": "^18.3.1",
@@ -30,8 +31,8 @@
     "react-use-storage": "^0.5.1"
   },
   "scripts": {
-    "start": "react-scripts start",
-    "build": "react-scripts build",
+    "start": "./env.sh -e .env.development -o ./public && BROWSER=none NODE_OPTIONS=--openssl-legacy-provider react-scripts start",
+    "build": "NODE_OPTIONS=--openssl-legacy-provider react-scripts build",
     "test": "react-scripts test",
     "eject": "react-scripts eject",
     "lint": "eslint ./src",
diff --git a/public/env-config.js b/public/env-config.js
new file mode 100644
index 0000000..b723f4b
--- /dev/null
+++ b/public/env-config.js
@@ -0,0 +1,8 @@
+window._env_ = {
+  REACT_APP_BASE_URL: "http://localhost:3000",
+  REACT_APP_KEYCLOAK_BASE_URL: "https://in-sylva.inrae.fr/keycloak/realms/in-sylva",
+  REACT_APP_KEYCLOAK_CLIENT_ID: "in-sylva.user.app",
+  REACT_APP_KEYCLOAK_CLIENT_SECRET: "oONCnrlp0zvFnXphZdbGhRUhADZvtu5D",
+  REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL: "https://in-sylva.inrae.fr/gatekeeper",
+  NODE_TLS_REJECT_UNAUTHORIZED: "0",
+}
diff --git a/public/index.html b/public/index.html
index 5e6c564..d605a92 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,12 +1,13 @@
 <!DOCTYPE html>
 <html lang="en">
-<head>
-  <meta charset="utf-8" />
-  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.png" />
-  <meta name="viewport" content="width=device-width, initial-scale=1" />
-  <meta name="theme-color" content="#000000" />
-  <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-  <!--
+  <head>
+    <meta charset="utf-8" />
+    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.png" />
+    <meta name="viewport"
+      content="width=device-width, initial-scale=1, shrink-to-fit=no" />
+    <meta name="theme-color" content="#000000" />
+    <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
+    <!--
       Notice the use of %PUBLIC_URL% in the tags above.
       It will be replaced with the URL of the `public` folder during the build.
       Only files inside the `public` folder can be referenced from the HTML.
@@ -15,11 +16,11 @@
       work correctly both with client-side routing and a non-root public URL.
       Learn how to configure a non-root public URL by running `npm run build`.
     -->
-  <title>IN-SYLVA Search</title>
-  <script src="%PUBLIC_URL%/env-config.js"></script>
-</head>
-<body>
-  <noscript>You need to enable JavaScript to run this app.</noscript>
-  <div id="root"></div>
-</body>
+    <title>IN-SYLVA Search</title>
+    <script src="%PUBLIC_URL%/env-config.js"></script>
+  </head>
+  <body style="font-family: 'Roboto', sans-serif;">
+    <noscript>You need to enable JavaScript to run this app.</noscript>
+    <div id="root"></div>
+  </body>
 </html>
diff --git a/public/locales/en/profile.json b/public/locales/en/profile.json
index 4daa91e..d0dc82c 100644
--- a/public/locales/en/profile.json
+++ b/public/locales/en/profile.json
@@ -46,7 +46,8 @@
   "groups": {
     "groupsList": "Existing groups",
     "groupName": "Name",
-    "groupDescription": "Description"
+    "groupDescription": "Description",
+    "groupMember": "Member ?"
   },
   "requestsList": {
     "requestsList": "Requests list",
diff --git a/public/locales/fr/maps.json b/public/locales/fr/maps.json
index 51f7f8d..072712e 100644
--- a/public/locales/fr/maps.json
+++ b/public/locales/fr/maps.json
@@ -25,8 +25,8 @@
     "title": "Ressources sélectionnées",
     "empty": "Sélectionnez des ressources pour les afficher ici",
     "actions": {
-      "openResourceFlyout": "Ouvrir la fiche de la resource",
-      "unselectResource": "Désélectionner la resource"
+      "openResourceFlyout": "Ouvrir la fiche de la ressource",
+      "unselectResource": "Déselectionner la resource"
     }
   }
 }
diff --git a/public/locales/fr/profile.json b/public/locales/fr/profile.json
index 22d784c..60412a9 100644
--- a/public/locales/fr/profile.json
+++ b/public/locales/fr/profile.json
@@ -46,7 +46,8 @@
   "groups": {
     "groupsList": "Groupes existants",
     "groupName": "Nom",
-    "groupDescription": "Description"
+    "groupDescription": "Description",
+    "groupMember": "Member ?"
   },
   "requestsList": {
     "requestsList": "Liste des requêtes",
diff --git a/src/App.js b/src/App.js
index 181dc86..80aa31b 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,7 +4,6 @@ import {
   createHashRouter,
   Route,
   createRoutesFromElements,
-  Navigate,
 } from 'react-router-dom';
 import Home from './pages/home';
 import Search from './pages/search';
@@ -16,9 +15,8 @@ const App = () => {
   const router = createHashRouter(
     createRoutesFromElements(
       <>
-        <Route path="/" element={<Navigate to="/home" />} />
         <Route errorElement={<ErrorBoundary />} element={<Layout />}>
-          <Route index path="/home" element={<Home />} />
+          <Route index path="/" element={<Home />} />
           <Route path="/search" element={<Search />} />
           <Route path="/profile" element={<Profile />} />
         </Route>
diff --git a/src/Utils.js b/src/Utils.js
index 7042c6f..65fc6c4 100644
--- a/src/Utils.js
+++ b/src/Utils.js
@@ -8,12 +8,6 @@ export class SearchField {
   }
 }
 
-export const getLoginUrl = () => {
-  return process.env.REACT_APP_IN_SYLVA_LOGIN_PORT
-    ? `${process.env.REACT_APP_IN_SYLVA_LOGIN_HOST}:${process.env.REACT_APP_IN_SYLVA_LOGIN_PORT}`
-    : `${window._env_.REACT_APP_IN_SYLVA_LOGIN_HOST}`;
-};
-
 export const removeNullFields = (standardFields) => {
   let standardFieldsNoNull = standardFields;
   let nullIndex;
@@ -47,28 +41,32 @@ export const getSections = (fields) => {
 };
 
 export const getFieldsBySection = (fields, fieldSection) => {
+  if (!fieldSection) {
+    return [];
+  }
+
   let filteredFields = [];
   fields.forEach((field) => {
     if (
       field.field_name &&
       field.field_name.split('.', 1)[0].replace(/_|\./g, ' ') === fieldSection.label
     ) {
-      if (field.sources.length) {
-        filteredFields.push({
-          label: field.field_name
-            .substring(field.field_name.indexOf('.') + 1)
-            .replace(/_|\./g, ' '),
-          color: 'danger',
-        });
-      } else {
-        filteredFields.push({
-          label: field.field_name
-            .substring(field.field_name.indexOf('.') + 1)
-            .replace(/_|\./g, ' '),
-          color: 'primary',
-        });
-      }
+      // if (field.sources.length) {
+      //   filteredFields.push({
+      //     label: field.field_name
+      //       .substring(field.field_name.indexOf('.') + 1)
+      //       .replace(/_|\./g, ' '),
+      //     color: 'danger',
+      //   });
+      // } else {
+      filteredFields.push({
+        label: field.field_name
+          .substring(field.field_name.indexOf('.') + 1)
+          .replace(/_|\./g, ' '),
+        color: 'primary',
+      });
     }
+    //}
   });
   return filteredFields;
 };
@@ -268,7 +266,7 @@ const buildDslClause = (clause, fields) => {
 
 // const buildDslQuery = (request, query, fields) => {
 //     let tmpQuery = query
-const buildDslQuery = (request, fields) => {
+export const buildDslQuery = (request, fields) => {
   let requestStr = request.replace(/ OR /g, ' | ').replace(/ AND /g, ' & ').trim();
   let splitRequest = splitString(requestStr, /\(/, /\)/, /&|\|/);
   let tmpQuery = '';
@@ -361,70 +359,6 @@ export const createAdvancedQueriesBySource = (
   const indicesLists = [];
   const publicFieldnames = [];
   const privateFields = [];
-  /* fields.forEach(field => {
-        if (field.ispublic) {
-            publicFieldnames.push(field.field_name)
-        } else {
-            privateFields.push(field)
-        }
-    })
-
-    if (selectedSources.length) {
-        selectedSources.forEach(source => {
-            queriedSourcesId.push(source.id)
-        })
-    } else {
-        sources.forEach(source => {
-            queriedSourcesId.push(source.id)
-        })
-    }
-
-    noPolicySourcesId = queriedSourcesId
-
-    //browse fields to create a map containing the fields available by source
-    //ie : [field1, field2] - [source1], [field1, field3, field4] - [source2], [field1] - []
-    //empty brackets means "the sources not affected by policies" at that point - this list is being compiled at the same time
-    if (privateFields.length) {
-        privateFields.forEach(field => {
-            field.sources.forEach(sourceId => {
-                if (queriedSourcesId.includes(sourceId)) {
-                    const index = findArray(sourcesLists, [sourceId])
-                    if (index >= 0) {
-                        fieldsLists[index].push(field.field_name)
-                    } else {
-                        fieldsLists.push([field.field_name])
-                        sourcesLists.push([sourceId])
-                    }
-                }
-                //filter no policy sources
-                if (noPolicySourcesId.includes(sourceId))
-                    noPolicySourcesId = removeArrayElement(noPolicySourcesId, noPolicySourcesId.indexOf(sourceId))
-            })
-        })
-        //add the public fields for every source
-        fieldsLists.forEach(fieldList => {
-            fieldList.push(...publicFieldnames)
-        })
-    }
-
-    const indexOfAllSources = findArray(sourcesLists, [])
-    //if there isn't any source with no policy, remove corresponding items in sourcesLists and fieldsLists
-    if (!noPolicySourcesId.length) {
-        sourcesLists = removeArrayElement(sourcesLists, indexOfAllSources)
-        fieldsLists = removeArrayElement(fieldsLists, indexOfAllSources)
-    } else {
-        sourcesLists = updateArrayElement(sourcesLists, indexOfAllSources, noPolicySourcesId)
-    }
-
-    //get elastic indices from sources id
-    sourcesLists.forEach(sourceList => {
-        const indicesList = []
-        sourceList.forEach(sourceId => {
-            const source = sources.find(src => src.id === sourceId)
-            indicesList.push(source.index_id)
-        })
-        indicesLists.push(indicesList)
-    }) */
 
   fields.forEach((field) => {
     if (field.ispublic) {
@@ -499,6 +433,7 @@ export const createAdvancedQueriesBySource = (
   });
 
   const queryContent = buildDslQuery(searchRequest, fields);
+  console.log('dslQuery', queryContent);
 
   sourcesLists.forEach((sourcesArray, index) => {
     let sourceParam = `"_source": [`;
@@ -517,104 +452,3 @@ export const createAdvancedQueriesBySource = (
   });
   return queries;
 };
-
-export const createBasicQueriesBySource = (
-  fields,
-  searchRequest,
-  selectedSources,
-  sources
-) => {
-  const queries = [];
-  let fieldsLists = [[]];
-  let sourcesLists = [[]];
-  let queriedSourcesId = [];
-  let noPolicySourcesId = [];
-  const indicesLists = [];
-  const publicFieldnames = [];
-  const privateFields = [];
-
-  fields.forEach((field) => {
-    if (field.ispublic) {
-      publicFieldnames.push(field.field_name);
-    } else {
-      privateFields.push(field);
-    }
-  });
-
-  if (selectedSources.length) {
-    selectedSources.forEach((source) => {
-      queriedSourcesId.push(source.id);
-    });
-  } else {
-    if (sources.length) {
-      sources.forEach((source) => {
-        queriedSourcesId.push(source.id);
-      });
-    }
-  }
-
-  noPolicySourcesId = queriedSourcesId;
-
-  //browse fields to create a map containing the fields available by source
-  //ie : [field1, field2] - [source1], [field1, field3, field4] - [source2], [field1] - []
-  //empty brackets means "the sources not affected by policies" at that point
-  if (privateFields.length) {
-    privateFields.forEach((field) => {
-      field.sources.forEach((sourceId) => {
-        if (queriedSourcesId.includes(sourceId)) {
-          const index = findArray(sourcesLists, [sourceId]);
-          if (index >= 0) {
-            fieldsLists[index].push(field.field_name);
-          } else {
-            fieldsLists.push([field.field_name]);
-            sourcesLists.push([sourceId]);
-          }
-        }
-        //filter no policy sources
-        if (noPolicySourcesId.includes(sourceId))
-          noPolicySourcesId = removeArrayElement(
-            noPolicySourcesId,
-            noPolicySourcesId.indexOf(sourceId)
-          );
-      });
-    });
-    //add the public fields for every source
-    fieldsLists.forEach((fieldList) => {
-      fieldList.push(...publicFieldnames);
-    });
-  }
-
-  const indexOfAllSources = findArray(sourcesLists, []);
-  //if there is no source with no policy, remove corresponding items in sourcesLists and fieldsLists
-  if (!noPolicySourcesId.length) {
-    sourcesLists = removeArrayElement(sourcesLists, indexOfAllSources);
-    fieldsLists = removeArrayElement(fieldsLists, indexOfAllSources);
-  } else {
-    sourcesLists = updateArrayElement(sourcesLists, indexOfAllSources, noPolicySourcesId);
-    fieldsLists = updateArrayElement(fieldsLists, indexOfAllSources, publicFieldnames);
-  }
-
-  //get elastic indices from sources id
-  sourcesLists.forEach((sourceList) => {
-    const indicesList = [];
-    sourceList.forEach((sourceId) => {
-      const source = sources.find((src) => src.id === sourceId);
-      indicesList.push(source.index_id);
-    });
-    indicesLists.push(indicesList);
-  });
-
-  sourcesLists.forEach((sourcesArray, index) => {
-    let sourceParam = `"_source": [`;
-    fieldsLists[index].forEach((fieldName) => {
-      sourceParam = `${sourceParam} "${fieldName}", `;
-    });
-    if (sourceParam.endsWith(', ')) {
-      sourceParam = sourceParam.substring(0, sourceParam.length - 2);
-    }
-    sourceParam = `${sourceParam}],`;
-    let query = `{ ${sourceParam} "query": { "multi_match": { "query": "${searchRequest}", "operator": "AND", "type": "cross_fields" } } }`;
-    queries.push({ indicesId: indicesLists[index], query: JSON.parse(query) });
-  });
-  return queries;
-};
diff --git a/src/actions/index.js b/src/actions/index.js
deleted file mode 100644
index 245f568..0000000
--- a/src/actions/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import * as user from './user';
-export { user };
diff --git a/src/actions/source.js b/src/actions/source.js
deleted file mode 100644
index cd208f5..0000000
--- a/src/actions/source.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { InSylvaSourceManagerClient } from '../context/InSylvaSourceManagerClient';
-import { InSylvaSearchClient } from '../context/InSylvaSearchClient';
-import { refreshToken } from '../context/UserContext';
-import { tokenTimedOut } from '../Utils';
-
-const ismClient = new InSylvaSourceManagerClient();
-ismClient.baseUrl = process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_HOST}:${process.env.REACT_APP_IN_SYLVA_SOURCE_MANAGER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_SOURCE_MANAGER_HOST}`;
-const isClient = new InSylvaSearchClient();
-isClient.baseUrl = process.env.REACT_APP_IN_SYLVA_SEARCH_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_SEARCH_HOST}:${process.env.REACT_APP_IN_SYLVA_SEARCH_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_SEARCH_HOST}`;
-
-export {
-  fetchPublicFields,
-  fetchUserPolicyFields,
-  fetchSources,
-  searchQuery,
-  getQueryCount,
-};
-
-async function fetchPublicFields() {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.publicFields();
-}
-
-async function fetchUserPolicyFields(kcId) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.userPolicyFields(kcId);
-}
-
-async function fetchSources(kcId) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  ismClient.token = sessionStorage.getItem('access_token');
-  return await ismClient.sourcesWithIndexes(kcId);
-}
-
-async function searchQuery(query) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  isClient.token = sessionStorage.getItem('access_token');
-  return await isClient.search(query);
-}
-
-async function getQueryCount(queries) {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    refreshToken();
-  }
-  isClient.token = sessionStorage.getItem('access_token');
-  return await isClient.count(queries);
-}
diff --git a/src/actions/user.js b/src/actions/user.js
deleted file mode 100644
index d5cc589..0000000
--- a/src/actions/user.js
+++ /dev/null
@@ -1,216 +0,0 @@
-import { InSylvaGatekeeperClient } from '../context/InSylvaGatekeeperClient';
-import { refreshToken } from '../context/UserContext';
-import { tokenTimedOut } from '../Utils';
-
-const igClient = new InSylvaGatekeeperClient();
-igClient.baseUrl = process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}:${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}`;
-
-export const findOneUser = async (id, request = igClient) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const user = await request.findOneUser(id);
-    if (user) {
-      return user;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const findOneUserWithGroupAndRole = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const user = await igClient.findOneUserWithGroupAndRole(kcId);
-    if (user) {
-      return user;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const getGroups = async () => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const groups = await igClient.getGroups();
-    if (groups) {
-      return groups;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const getRoles = async () => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const roles = await igClient.findRole();
-    if (roles) {
-      return roles;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const sendMail = async (subject, message, request = igClient) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    await request.sendMail(subject, message);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserDetails = async (kcId) => {
-  try {
-    return await igClient.getUserDetails(kcId);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserRequests = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const requests = await igClient.getUserRequests(kcId);
-    if (requests) {
-      return requests;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const createUserRequest = async (kcId, message) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.createUserRequest(kcId, message);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserRequest = async (requestId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.deleteUserRequest(requestId);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const addUserHistory = async (kcId, query, name, uiStructure, description) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const jsonUIStructure = JSON.stringify(uiStructure);
-    return await igClient.addUserHistory(kcId, query, name, jsonUIStructure, description);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserHistory = async (kcId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const history = await igClient.userHistory(kcId);
-    if (history) {
-      return history;
-    }
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserHistory = async (id) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    await igClient.deleteUserHistory(id);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const fetchUserFieldsDisplaySettings = async (userId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    const row = await igClient.fetchUserFieldsDisplaySettings(userId);
-    return row.std_fields_ids;
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const createUserFieldsDisplaySettings = async (userId, stdFieldsIds) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.createUserFieldsDisplaySettings(userId, stdFieldsIds);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const updateUserFieldsDisplaySettings = async (userId, stdFieldsIds) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.updateUserFieldsDisplaySettings(userId, stdFieldsIds);
-  } catch (error) {
-    console.error(error);
-  }
-};
-
-export const deleteUserFieldsDisplaySettings = async (userId) => {
-  if (tokenTimedOut(process.env.REACT_KEYCLOAK_TOKEN_VALIDITY)) {
-    await refreshToken();
-  }
-  igClient.token = sessionStorage.getItem('access_token');
-  try {
-    return await igClient.deleteUserFieldsDisplaySettings(userId);
-  } catch (error) {
-    console.error(error);
-  }
-};
diff --git a/src/components/Header/Header.js b/src/components/Header/Header.js
index 2e653c1..dddf2ba 100644
--- a/src/components/Header/Header.js
+++ b/src/components/Header/Header.js
@@ -17,7 +17,7 @@ const routes = [
   {
     id: 0,
     label: 'home',
-    href: '/home',
+    href: '/',
   },
   {
     id: 1,
diff --git a/src/components/Header/HeaderUserMenu.js b/src/components/Header/HeaderUserMenu.js
index 0b4a4a9..cbf3f37 100644
--- a/src/components/Header/HeaderUserMenu.js
+++ b/src/components/Header/HeaderUserMenu.js
@@ -1,4 +1,5 @@
 import React, { useEffect, useState } from 'react';
+import { useAuth } from 'oidc-react';
 import {
   EuiAvatar,
   EuiFlexGroup,
@@ -8,11 +9,11 @@ import {
   EuiPopover,
   EuiButtonIcon,
 } from '@elastic/eui';
-import { signOut } from '../../context/UserContext';
 import { useTranslation } from 'react-i18next';
 import { NavLink } from 'react-router-dom';
 
 const HeaderUserMenu = () => {
+  const auth = useAuth();
   const { t } = useTranslation('header');
   const [isOpen, setIsOpen] = useState(false);
   const [username, setUsername] = useState('');
@@ -26,7 +27,7 @@ const HeaderUserMenu = () => {
   };
 
   useEffect(() => {
-    setUsername(sessionStorage.getItem('username'));
+    setUsername(auth.userData?.profile?.preferred_username || '');
   }, []);
 
   const HeaderUserButton = (
@@ -63,7 +64,7 @@ const HeaderUserMenu = () => {
                   </NavLink>
                 </EuiFlexItem>
                 <EuiFlexItem grow={false}>
-                  <NavLink to={'/'} onClick={() => signOut()}>
+                  <NavLink to={'/'} onClick={() => auth.signOutRedirect()}>
                     {t('header:userMenu.logOutButton')}
                   </NavLink>
                 </EuiFlexItem>
diff --git a/src/context/InSylvaGatekeeperClient.js b/src/context/InSylvaGatekeeperClient.js
deleted file mode 100644
index 26f1078..0000000
--- a/src/context/InSylvaGatekeeperClient.js
+++ /dev/null
@@ -1,138 +0,0 @@
-class InSylvaGatekeeperClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      Authorization: `Bearer ${this.token}`,
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  async getGroups() {
-    const path = `/user/groups`;
-    return await this.post('POST', `${path}`, {});
-  }
-
-  async getUserRequests(kcId) {
-    const path = `/user/list-requests-by-user`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async createUserRequest(kcId, message) {
-    const path = `/user/create-request`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-      message,
-    });
-  }
-
-  async deleteUserRequest(id) {
-    const path = `/user/delete-request`;
-    return await this.post('DELETE', `${path}`, { id });
-  }
-
-  async findRole() {
-    const path = `/role/find`;
-    return await this.post('GET', `${path}`);
-  }
-
-  async kcId({ email }) {
-    const path = `/user/kcid`;
-    return await this.post('POST', `${path}`, {
-      email,
-    });
-  }
-
-  async sendMail(subject, message) {
-    const path = `/user/send-mail`;
-
-    await this.post('POST', `${path}`, {
-      subject,
-      message,
-    });
-  }
-
-  async findOneUser(id) {
-    const path = `/user/findOne`;
-    return await this.post('POST', `${path}`, {
-      id,
-    });
-  }
-
-  // Returns an array containing objects for each user group.
-  async findOneUserWithGroupAndRole(kcId) {
-    const path = `/user/one-with-groups-and-roles`;
-    return await this.post('POST', `${path}`, {
-      id: kcId,
-    });
-  }
-
-  async getUserDetails(kcId) {
-    const path = `/user/detail`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async addUserHistory(kcId, query, name, uiStructure, description) {
-    const path = `/user/add-history`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-      query,
-      name,
-      uiStructure,
-      description,
-    });
-  }
-
-  async userHistory(kcId) {
-    const path = `/user/fetch-history`;
-    return await this.post('POST', `${path}`, {
-      kcId,
-    });
-  }
-
-  async deleteUserHistory(id) {
-    const path = `/user/delete-history`;
-    await this.post('POST', `${path}`, {
-      id,
-    });
-  }
-
-  // GET Fetches user fields display settings
-  async fetchUserFieldsDisplaySettings(userId) {
-    return await this.post('GET', `/user/fields-display-settings/${userId}`);
-  }
-
-  // POST Creates user fields display settings
-  async createUserFieldsDisplaySettings(userId, stdFieldsIds) {
-    return await this.post('POST', `/user/fields-display-settings/`, {
-      userId,
-      stdFieldsIds,
-    });
-  }
-
-  // PUT Updates user fields display settings
-  async updateUserFieldsDisplaySettings(userId, stdFieldsIds) {
-    return await this.post('PUT', `/user/fields-display-settings/`, {
-      userId,
-      stdFieldsIds,
-    });
-  }
-
-  // DELETE Remove user fields display settings
-  async deleteUserFieldsDisplaySettings(userId) {
-    return await this.post('DELETE', `/user/fields-display-settings/${userId}`);
-  }
-}
-
-InSylvaGatekeeperClient.prototype.baseUrl = null;
-InSylvaGatekeeperClient.prototype.token = null;
-export { InSylvaGatekeeperClient };
diff --git a/src/context/InSylvaKeycloakClient.js b/src/context/InSylvaKeycloakClient.js
deleted file mode 100644
index 092f27c..0000000
--- a/src/context/InSylvaKeycloakClient.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { getLoginUrl } from '../Utils';
-
-class InSylvaKeycloakClient {
-  async post(path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/x-www-form-urlencoded',
-      // "Access-Control-Allow-Methods": "GET,HEAD,PUT,PATCH,POST,DELETE",
-      // "Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept, Authorization"
-    };
-    let formBody = [];
-    for (const property in requestContent) {
-      const encodedKey = encodeURIComponent(property);
-      const encodedValue = encodeURIComponent(requestContent[property]);
-      formBody.push(encodedKey + '=' + encodedValue);
-    }
-    formBody = formBody.join('&');
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method: 'POST',
-      headers,
-      body: formBody,
-      mode: 'cors',
-    });
-    if (!response.ok) {
-      await this.logout();
-      sessionStorage.removeItem('kcId');
-      sessionStorage.removeItem('access_token');
-      sessionStorage.removeItem('refresh_token');
-      window.location.replace(getLoginUrl() + '?requestType=search');
-    }
-    if (response.statusText !== 'No Content') {
-      return await response.json();
-    }
-  }
-
-  async refreshToken({
-    realm = this.realm,
-    client_id = this.client_id,
-    grant_type = 'refresh_token',
-    refresh_token,
-  }) {
-    const path = `/auth/realms/${realm}/protocol/openid-connect/token`;
-    const token = await this.post(`${path}`, {
-      client_id,
-      grant_type,
-      refresh_token,
-    });
-    return { token };
-  }
-
-  async logout() {
-    const refresh_token = sessionStorage.getItem('refresh_token');
-    if (refresh_token) {
-      const client_id = this.client_id;
-      await this.post(`/auth/realms/${this.realm}/protocol/openid-connect/logout`, {
-        client_id,
-        refresh_token,
-      });
-    }
-  }
-}
-
-InSylvaKeycloakClient.prototype.baseUrl = null;
-InSylvaKeycloakClient.prototype.client_id = null;
-InSylvaKeycloakClient.prototype.grant_type = null;
-InSylvaKeycloakClient.prototype.realm = null;
-export { InSylvaKeycloakClient };
diff --git a/src/context/InSylvaSearchClient.js b/src/context/InSylvaSearchClient.js
deleted file mode 100644
index ffdc831..0000000
--- a/src/context/InSylvaSearchClient.js
+++ /dev/null
@@ -1,67 +0,0 @@
-class InSylvaSearchClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      'Access-Control-Allow-Origin': '*',
-      Authorization: `Bearer ${this.token}`,
-      'Access-Control-Max-Age': 86400,
-      // 'Access-Control-Allow-Methods': 'GET, POST, PUT, PATCH',
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  /* async search(query, indices) {
-        const indicesId = []
-        indices.forEach(index => {
-            indicesId.push(index.index_id)
-        })
-        const path = `/search`;
-        const result = await this.post('POST', `${path}`, {
-            indicesId,
-            query
-        });
-        return result;
-    } */
-
-  async search(queries) {
-    let finalResult = [];
-
-    for (let i = 0; i < queries.length; i++) {
-      const indicesId = queries[i].indicesId;
-      const query = queries[i].query;
-      const result = await this.post('POST', '/scroll-search', {
-        indicesId,
-        query,
-      });
-      if (!result.statusCode) {
-        finalResult.push(...result);
-      }
-    }
-    return finalResult;
-  }
-
-  async count(queries) {
-    let finalResult = 0;
-    for (let i = 0; i < queries.length; i++) {
-      const indicesId = queries[i].indicesId;
-      const query = queries[i].query;
-      const path = `/count`;
-      const result = await this.post('POST', `${path}`, {
-        indicesId,
-        query,
-      });
-      finalResult = finalResult + result.count;
-    }
-    return finalResult;
-  }
-}
-
-InSylvaSearchClient.prototype.baseUrl = null;
-InSylvaSearchClient.prototype.token = null;
-export { InSylvaSearchClient };
diff --git a/src/context/InSylvaSourceManagerClient.js b/src/context/InSylvaSourceManagerClient.js
deleted file mode 100644
index ceffbd6..0000000
--- a/src/context/InSylvaSourceManagerClient.js
+++ /dev/null
@@ -1,34 +0,0 @@
-class InSylvaSourceManagerClient {
-  async post(method, path, requestContent) {
-    const headers = {
-      'Content-Type': 'application/json',
-      Authorization: `Bearer ${this.token}`,
-    };
-    const response = await fetch(`${this.baseUrl}${path}`, {
-      method,
-      headers,
-      body: JSON.stringify(requestContent),
-      mode: 'cors',
-    });
-    return await response.json();
-  }
-
-  async publicFields() {
-    const path = `/publicFieldList`;
-    return this.post('GET', `${path}`);
-  }
-
-  async userPolicyFields(userId) {
-    const path = `/policy-stdfields`;
-    return this.post('POST', `${path}`, { userId });
-  }
-
-  async sourcesWithIndexes(kc_id) {
-    const path = `/source_indexes`;
-    return this.post('POST', `${path}`, { kc_id });
-  }
-}
-
-InSylvaSourceManagerClient.prototype.baseUrl = null;
-InSylvaSourceManagerClient.prototype.token = null;
-export { InSylvaSourceManagerClient };
diff --git a/src/context/UserContext.js b/src/context/UserContext.js
deleted file mode 100644
index 1b6d8b7..0000000
--- a/src/context/UserContext.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import React, { createContext, useContext, useReducer } from 'react';
-import { InSylvaGatekeeperClient } from './InSylvaGatekeeperClient';
-import { InSylvaKeycloakClient } from './InSylvaKeycloakClient';
-import { getLoginUrl } from '../Utils';
-import { fetchUserDetails, findOneUser } from '../actions/user';
-
-const UserStateContext = createContext(null);
-const UserDispatchContext = createContext(null);
-
-const igClient = new InSylvaGatekeeperClient();
-igClient.baseUrl = process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}:${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GATEKEEPER_HOST}`;
-
-const ikcClient = new InSylvaKeycloakClient();
-ikcClient.baseUrl = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_KEYCLOAK_HOST}:${process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT}`
-  : `${window._env_.REACT_APP_IN_SYLVA_KEYCLOAK_HOST}`;
-ikcClient.realm = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_REALM}`
-  : `${window._env_.REACT_APP_IN_SYLVA_REALM}`;
-ikcClient.client_id = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_CLIENT_ID}`
-  : `${window._env_.REACT_APP_IN_SYLVA_CLIENT_ID}`;
-ikcClient.grant_type = process.env.REACT_APP_IN_SYLVA_KEYCLOAK_PORT
-  ? `${process.env.REACT_APP_IN_SYLVA_GRANT_TYPE}`
-  : `${window._env_.REACT_APP_IN_SYLVA_GRANT_TYPE}`;
-
-function userReducer(state, action) {
-  switch (action.type) {
-    case 'USER_LOGGED_IN':
-      return { ...state, isAuthenticated: true };
-    case 'SIGN_OUT_SUCCESS':
-      return { ...state, isAuthenticated: false };
-    case 'EXPIRED_SESSION':
-      return { ...state, isAuthenticated: false };
-    case 'USER_NOT_LOGGED_IN':
-      return { ...state, isAuthenticated: false };
-    case 'STD_FIELDS_SUCCESS':
-      return { ...state, isAuthenticated: true };
-    case 'STD_FIELDS_FAILURE':
-      return { ...state, isAuthenticated: true };
-    default: {
-      throw new Error(`Unhandled action type: ${action.type}`);
-    }
-  }
-}
-
-function UserProvider({ children }) {
-  const [state, dispatch] = useReducer(userReducer, {
-    isAuthenticated: !!sessionStorage.getItem('access_token'),
-  });
-
-  return (
-    <UserStateContext.Provider value={state}>
-      <UserDispatchContext.Provider value={dispatch}>
-        {children}
-      </UserDispatchContext.Provider>
-    </UserStateContext.Provider>
-  );
-}
-
-function useUserState() {
-  const context = useContext(UserStateContext);
-  if (context === undefined) {
-    throw new Error('useUserState must be used within a UserProvider');
-  }
-  return context;
-}
-
-function useUserDispatch() {
-  const context = useContext(UserDispatchContext);
-  if (context === undefined) {
-    throw new Error('useUserDispatch must be used within a UserProvider');
-  }
-  return context;
-}
-
-const getUserIdFromKcId = async (kcId) => {
-  if (!kcId) {
-    return;
-  }
-  const userDetails = await fetchUserDetails(kcId);
-  if (!userDetails) {
-    return;
-  }
-  return userDetails.id;
-};
-
-const checkUserLogin = async (kcId, accessToken, refreshToken) => {
-  if (!kcId || !accessToken || !refreshToken) {
-    return;
-  }
-  const userId = await getUserIdFromKcId(kcId);
-  if (userId) {
-    sessionStorage.setItem('userId', userId.toString());
-  }
-  const user = await findOneUser(kcId);
-  if (user) {
-    sessionStorage.setItem('username', user.username);
-    sessionStorage.setItem('email', user.email);
-  }
-  sessionStorage.setItem('kcId', kcId);
-  sessionStorage.setItem('access_token', accessToken);
-  sessionStorage.setItem('refresh_token', refreshToken);
-  if (!sessionStorage.getItem('token_refresh_time')) {
-    sessionStorage.setItem('token_refresh_time', Date.now().toString());
-  }
-};
-
-async function refreshToken() {
-  if (!sessionStorage.getItem('kcId')) {
-    return;
-  }
-  setTimeout(async () => {
-    const result = await ikcClient.refreshToken({
-      refresh_token: sessionStorage.getItem('refresh_token'),
-    });
-    if (result) {
-      sessionStorage.setItem('access_token', result.token.access_token);
-      sessionStorage.setItem('token_refresh_time', Date.now().toString());
-    }
-  }, 3000);
-}
-
-async function signOut() {
-  await ikcClient.logout();
-  sessionStorage.removeItem('kcId');
-  sessionStorage.removeItem('access_token');
-  sessionStorage.removeItem('refresh_token');
-  window.location.replace(getLoginUrl() + '?requestType=search');
-}
-
-export {
-  UserProvider,
-  useUserState,
-  useUserDispatch,
-  checkUserLogin,
-  refreshToken,
-  signOut,
-};
diff --git a/src/contexts/GatekeeperContext.js b/src/contexts/GatekeeperContext.js
new file mode 100644
index 0000000..9604361
--- /dev/null
+++ b/src/contexts/GatekeeperContext.js
@@ -0,0 +1,19 @@
+import React, { createContext, useContext } from 'react';
+import { InSylvaGatekeeperClient } from '../services/GatekeeperService';
+import { useUserInfo } from './TokenContext';
+
+const GatekeeperContext = createContext(null);
+
+export const useGatekeeper = () => {
+  const context = useContext(GatekeeperContext);
+  return context;
+};
+
+export const GatekeeperProvider = ({ children }) => {
+  const getUserInfo = useUserInfo();
+  const client = new InSylvaGatekeeperClient(getUserInfo);
+
+  return (
+    <GatekeeperContext.Provider value={client}>{children}</GatekeeperContext.Provider>
+  );
+};
diff --git a/src/contexts/TokenContext.js b/src/contexts/TokenContext.js
new file mode 100644
index 0000000..dfbd78f
--- /dev/null
+++ b/src/contexts/TokenContext.js
@@ -0,0 +1,52 @@
+import React, { createContext, useContext } from 'react';
+import { useAuth } from 'oidc-react';
+import TokenService from '../services/TokenService';
+
+const TokenContext = createContext(null);
+
+export const useUserInfo = () => {
+  const context = useContext(TokenContext);
+  return context;
+};
+
+export const TokenProvider = ({ children }) => {
+  const auth = useAuth();
+
+  const getUserInfo = async () => {
+    //console.log('Getting access token');
+    if (!auth) {
+      //console.log('Auth not found');
+      return null;
+    }
+
+    if (auth.isLoading) {
+      //console.log('Auth is loading');
+      return null;
+    }
+
+    if (!auth.userData || !auth.userData.access_token) {
+      //console.log('User data not found');
+      await auth.signOutRedirect();
+    }
+
+    const access_token = auth.userData.access_token;
+    //console.log(access_token);
+
+    let userInfo = await new TokenService().getUserInfo(access_token);
+    if (!userInfo) {
+      //console.log('User info not found');
+      const refreshed = await new TokenService().refreshToken(
+        auth.userData.refresh_token
+      );
+      if (!refreshed) {
+        //console.log('Error refreshing token');
+        await auth.signOutRedirect();
+      }
+      userInfo = await new TokenService().getUserInfo(refreshed.access_token);
+    }
+    //console.log('User info:', userInfo);
+    return { ...userInfo, ...auth.userData };
+  };
+
+  return <TokenContext.Provider value={getUserInfo}>{children}</TokenContext.Provider>;
+};
diff --git a/src/i18n.js b/src/i18n.js
index d422da2..5f49f6e 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -10,7 +10,7 @@ i18n
     fallbackLng: 'fr',
     ns: 'common',
     defaultNS: 'common',
-    debug: true,
+    debug: false,
     load: 'languageOnly',
     interpolation: {
       // not needed for react as it escapes by default
diff --git a/src/index.js b/src/index.js
index 3944da2..1e27e35 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,32 +1,38 @@
 import React, { Suspense } from 'react';
-import '@elastic/eui/dist/eui_theme_light.css';
-import { UserProvider, checkUserLogin } from './context/UserContext';
+import { createRoot } from 'react-dom/client';
+import { AuthProvider } from 'oidc-react';
 import App from './App';
-import { getLoginUrl, getUrlParam, redirect } from './Utils.js';
 import './i18n';
 import Loading from './components/Loading';
-import { createRoot } from 'react-dom/client';
 import { EuiProvider } from '@elastic/eui';
+import '@elastic/eui/dist/eui_theme_light.css';
+import { GatekeeperProvider } from './contexts/GatekeeperContext';
+import { TokenProvider } from './contexts/TokenContext';
 
-const userId = getUrlParam('kcId', '');
-const accessToken = getUrlParam('accessToken', '');
-let refreshToken = getUrlParam('refreshToken', '');
-if (refreshToken.includes('#/search')) {
-  refreshToken = refreshToken.substring(0, refreshToken.indexOf('#'));
-}
-checkUserLogin(userId, accessToken, refreshToken);
-
-if (sessionStorage.getItem('access_token')) {
-  const root = createRoot(document.getElementById('root'));
-  root.render(
-    <UserProvider>
-      <Suspense fallback={<Loading />}>
-        <EuiProvider colorMode={'light'}>
-          <App />
-        </EuiProvider>
-      </Suspense>
-    </UserProvider>
-  );
-} else {
-  redirect(getLoginUrl() + '?requestType=search');
-}
+const root = createRoot(document.getElementById('root'));
+root.render(
+  <AuthProvider
+    authority={`${process.env.REACT_APP_KEYCLOAK_BASE_URL}`}
+    clientId={`${process.env.REACT_APP_KEYCLOAK_CLIENT_ID}`}
+    clientSecret={`${process.env.REACT_APP_KEYCLOAK_CLIENT_SECRET}`}
+    redirectUri={`${process.env.REACT_APP_BASE_URL}`}
+    onBeforeSignIn={() => {
+      const redirectUri = window.location.hash;
+      return { postLoginRedirect: redirectUri };
+    }}
+    onSignIn={async (user) => {
+      const postLoginRedirect = user.state?.postLoginRedirect;
+      window.location.href = process.env.REACT_APP_BASE_URL + '/' + postLoginRedirect;
+    }}
+  >
+    <TokenProvider>
+      <GatekeeperProvider>
+        <Suspense fallback={<Loading />}>
+          <EuiProvider colorMode={'light'}>
+            <App />
+          </EuiProvider>
+        </Suspense>
+      </GatekeeperProvider>
+    </TokenProvider>
+  </AuthProvider>
+);
diff --git a/src/pages/profile/GroupSettings.js b/src/pages/profile/GroupSettings.js
index 1eeb85b..90e2fe9 100644
--- a/src/pages/profile/GroupSettings.js
+++ b/src/pages/profile/GroupSettings.js
@@ -10,56 +10,50 @@ import {
   EuiFlexGroup,
   EuiSpacer,
 } from '@elastic/eui';
-import { getGroups, sendMail, createUserRequest } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
-import styles from './styles';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const GroupSettings = ({ userGroups }) => {
+  const getUserInfo = useUserInfo();
+  const client = useGatekeeper();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [groups, setGroups] = useState([]);
   const [selectedUserGroups, setSelectedUserGroups] = useState([]);
   const [valueError, setValueError] = useState(undefined);
 
   useEffect(() => {
-    setSelectedUserGroups(userGroups);
+    const getUserGroups = async () => {
+      if (!client) {
+        return;
+      }
+      const groupResults = await client.getGroups();
+      const mappedGroups = groupResults.map((group) => {
+        return {
+          id: group.id,
+          label: group.name,
+          description: group.description,
+          member: userGroups.some((userGroup) => userGroup.id === group.id) ? 'X' : '',
+        };
+      });
+      setGroups(mappedGroups);
+    };
     getUserGroups();
   }, []);
 
   const groupColumns = [
     { field: 'label', name: t('profile:groups.groupName'), width: '30%' },
     { field: 'description', name: t('profile:groups.groupDescription') },
+    {
+      field: 'member',
+      name: t('profile:groups.groupMember'),
+      width: '10%',
+      align: 'center',
+    },
   ];
 
-  const getUserGroups = () => {
-    getGroups().then((groupsResult) => {
-      const groupsArray = [];
-      groupsResult.forEach((group) => {
-        groupsArray.push({
-          id: group.id,
-          label: group.name,
-          description: group.description,
-        });
-      });
-      setGroups(groupsArray);
-    });
-  };
-
-  const getUserGroupLabels = (groups) => {
-    let labelList = '';
-    if (!groups || groups.length === 0) {
-      return labelList;
-    }
-    groups.forEach((group) => {
-      labelList = `${labelList} ${group.label},`;
-    });
-    if (labelList.endsWith(',')) {
-      labelList = labelList.substring(0, labelList.length - 1);
-    }
-    return labelList;
-  };
-
   const onValueSearchChange = (value, hasMatchingOptions) => {
     if (value.length !== 0 && !hasMatchingOptions) {
       setValueError(t('profile:groupRequests.invalidOption'));
@@ -67,13 +61,14 @@ const GroupSettings = ({ userGroups }) => {
   };
 
   const onSendGroupRequest = async () => {
+    const userInfo = await getUserInfo();
     if (!selectedUserGroups || selectedUserGroups.length === 0) {
       return;
     }
-    const groupList = getUserGroupLabels(selectedUserGroups);
-    const message = `The user ${sessionStorage.getItem('username')} (${sessionStorage.getItem('email')}) has made a request to be part of these groups : ${groupList}.`;
-    const result = await createUserRequest(sessionStorage.getItem('kcId'), message);
-    await sendMail('User group request', message);
+    const message = `The user ${userInfo.email} has made a request to be part of these groups : ${selectedUserGroups
+      .map((group) => group.label)
+      .join(', ')}.`;
+    const result = await client.createUserRequest(message, userInfo.sub);
     if (result.error) {
       toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
     } else {
@@ -82,61 +77,56 @@ const GroupSettings = ({ userGroups }) => {
   };
 
   return (
-    <EuiFlexGroup>
-      <EuiFlexItem>
-        <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-          <EuiTitle size="s">
-            <h3>{t('profile:groups.groupsList')}</h3>
-          </EuiTitle>
-          <EuiSpacer size={'l'} />
-          <EuiBasicTable items={groups} columns={groupColumns} />
-        </EuiPanel>
-      </EuiFlexItem>
+    userGroups &&
+    userGroups.length > 0 && (
+      <EuiFlexGroup>
+        <EuiFlexItem>
+          <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
+            <EuiTitle size="s">
+              <h3>{t('profile:groups.groupsList')}</h3>
+            </EuiTitle>
+            <EuiSpacer size={'l'} />
+            <EuiBasicTable items={groups} columns={groupColumns} />
+          </EuiPanel>
+        </EuiFlexItem>
 
-      <EuiFlexItem>
-        <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
-          <EuiTitle size="s">
-            <h3>{t('profile:groupRequests.requestGroupAssignment')}</h3>
-          </EuiTitle>
-          <EuiSpacer size={'l'} />
-          {userGroups ? (
-            <p
-              style={styles.currentRoleOrGroupText}
-            >{`${t('profile:groupRequests.currentGroups')} ${getUserGroupLabels(userGroups)}`}</p>
-          ) : (
-            <p>{t('profile:groupRequests.noGroup')}</p>
-          )}
-          <EuiSpacer size={'l'} />
-          <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
-            <EuiComboBox
-              placeholder={t('profile:groupRequests.selectGroup')}
-              options={groups}
-              selectedOptions={selectedUserGroups}
-              onChange={(selectedOptions) => {
-                setValueError(undefined);
-                setSelectedUserGroups(selectedOptions);
-              }}
-              onSearchChange={onValueSearchChange}
-            />
-          </EuiFormRow>
-          <EuiSpacer size={'l'} />
-
-          <EuiFlexItem>
-            <div>
-              <EuiButton
-                disabled={selectedUserGroups.length === 0}
-                onClick={() => {
-                  onSendGroupRequest();
+        <EuiFlexItem>
+          <EuiPanel paddingSize="l" hasShadow={false} hasBorder={true}>
+            <EuiTitle size="s">
+              <h3>{t('profile:groupRequests.requestGroupAssignment')}</h3>
+            </EuiTitle>
+            <EuiSpacer size={'l'} />
+            <EuiFormRow error={valueError} isInvalid={valueError !== undefined}>
+              <EuiComboBox
+                placeholder={t('profile:groupRequests.selectGroup')}
+                options={groups.filter((group) => group.member != 'X')}
+                selectedOptions={selectedUserGroups}
+                onChange={(selectedOptions) => {
+                  setValueError(undefined);
+                  setSelectedUserGroups(selectedOptions);
                 }}
-                fill
-              >
-                {t('common:validationActions.send')}
-              </EuiButton>
-            </div>
-          </EuiFlexItem>
-        </EuiPanel>
-      </EuiFlexItem>
-    </EuiFlexGroup>
+                onSearchChange={onValueSearchChange}
+              />
+            </EuiFormRow>
+            <EuiSpacer size={'l'} />
+
+            <EuiFlexItem>
+              <div>
+                <EuiButton
+                  disabled={selectedUserGroups.length === 0}
+                  onClick={() => {
+                    onSendGroupRequest();
+                  }}
+                  fill
+                >
+                  {t('common:validationActions.send')}
+                </EuiButton>
+              </div>
+            </EuiFlexItem>
+          </EuiPanel>
+        </EuiFlexItem>
+      </EuiFlexGroup>
+    )
   );
 };
 
diff --git a/src/pages/profile/MyProfile.js b/src/pages/profile/MyProfile.js
index 57bf89a..a4d5ac5 100644
--- a/src/pages/profile/MyProfile.js
+++ b/src/pages/profile/MyProfile.js
@@ -10,7 +10,7 @@ import {
   EuiTitle,
   EuiIconTip,
 } from '@elastic/eui';
-import { deleteUserRequest } from '../../actions/user';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
 import { buildFieldName } from '../../Utils';
 import BulletPointList from '../../components/BulletPointList/BulletPointList';
 import { toast } from 'react-toastify';
@@ -25,6 +25,7 @@ const MyProfile = ({
   publicFields,
   getUserRequests,
 }) => {
+  const client = useGatekeeper();
   const { t } = useTranslation(['profile']);
 
   const MyProfileCustomPanel = ({
@@ -70,9 +71,9 @@ const MyProfile = ({
   };
 
   const onDeleteRequest = async (request) => {
-    const result = await deleteUserRequest(request.id);
-    if (result.error) {
-      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
+    const { success, message } = await client.deleteUserRequest(request.id);
+    if (!success) {
+      toast.error(<ToastMessage title={t('validation:error')} message={message} />);
     } else {
       toast.success(t('profile:requestsList.requestCanceled'));
       // Refresh requests table
@@ -116,9 +117,7 @@ const MyProfile = ({
     if (!userGroups || userGroups.length === 0) {
       return <p>{t('profile:groupRequests.noGroup')}</p>;
     }
-    const listItems = userGroups.map((group, index) => (
-      <li key={index}>{group.label}</li>
-    ));
+    const listItems = userGroups.map((group, index) => <li key={index}>{group.name}</li>);
     return <BulletPointList>{listItems}</BulletPointList>;
   };
 
diff --git a/src/pages/profile/Profile.js b/src/pages/profile/Profile.js
index 81c0d26..111b6c3 100644
--- a/src/pages/profile/Profile.js
+++ b/src/pages/profile/Profile.js
@@ -5,14 +5,12 @@ import UserFieldsDisplaySettings from './UserFieldsDisplaySettings';
 import GroupSettings from './GroupSettings';
 import RoleSettings from './RoleSettings';
 import MyProfile from './MyProfile';
-import {
-  fetchUserFieldsDisplaySettings,
-  fetchUserRequests,
-  findOneUserWithGroupAndRole,
-} from '../../actions/user';
-import { fetchPublicFields } from '../../actions/source';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const Profile = () => {
+  const getUserInfo = useUserInfo();
+  const client = useGatekeeper();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [userGroups, setUserGroups] = useState([]);
@@ -21,56 +19,38 @@ const Profile = () => {
   const [fieldsDisplaySettings, setFieldsDisplaySettings] = useState(null);
   const [publicFields, setPublicFields] = useState([]);
 
-  useEffect(() => {
-    findOneUserWithGroupAndRole(sessionStorage.getItem('kcId')).then((result) => {
-      if (result) {
-        if (result[0]) {
-          setUserRole(result[0].rolename);
-        }
-        const userGroupList = [];
-        result.forEach((userGroup) => {
-          if (userGroup.groupname) {
-            userGroupList.push({
-              id: userGroup.groupid,
-              label: userGroup.groupname,
-              description: userGroup.groupdescription,
-            });
-          }
-        });
-        setUserGroups(userGroupList);
-      }
-    });
-    getUserRequests();
-    getUserFieldsDisplaySettings();
-    getPublicFields();
-  }, []);
-
-  const getUserRequests = () => {
-    fetchUserRequests(sessionStorage.getItem('kcId')).then((userRequests) => {
-      if (userRequests) {
-        setUserRequests([...userRequests]);
-      }
-    });
-  };
-
-  const getUserFieldsDisplaySettings = () => {
-    fetchUserFieldsDisplaySettings(sessionStorage.getItem('userId')).then(
-      (userSettings) => {
-        if (userSettings) {
-          setFieldsDisplaySettings(userSettings);
-        }
-      }
+  const fetchUser = async () => {
+    const { sub } = await getUserInfo();
+    const user = await client.findUserBySub(sub);
+    if (!user.id) {
+      return;
+    }
+    const { groups, roles, requests, display_fields } = user;
+    setUserGroups(
+      groups.map((group) => {
+        return {
+          id: group.group.id,
+          name: group.group.name,
+        };
+      })
     );
+    setUserRole(roles[0]?.role?.name);
+    setUserRequests(requests);
+    setFieldsDisplaySettings(display_fields.map((field) => field.std_field_id));
   };
 
-  const getPublicFields = () => {
-    fetchPublicFields().then((publicFieldsResults) => {
-      if (publicFieldsResults) {
-        setPublicFields(publicFieldsResults);
-      }
-    });
+  const fetchFields = async () => {
+    const fields = await client.getPublicFields();
+    setPublicFields(fields);
   };
 
+  useEffect(() => {
+    if (client && selectedTabNumber === 0) {
+      fetchUser();
+      fetchFields();
+    }
+  }, [selectedTabNumber]);
+
   const Tab = ({ children, description }) => {
     const [showCallOut, setShowCallOut] = useState(true);
 
@@ -113,7 +93,7 @@ const Profile = () => {
             userRequests={userRequests}
             fieldsDisplaySettingsIds={fieldsDisplaySettings}
             publicFields={publicFields}
-            getUserRequests={() => getUserRequests()}
+            getUserRequests={() => fetchUser()}
           />
         </Tab>
       ),
diff --git a/src/pages/profile/RoleSettings.js b/src/pages/profile/RoleSettings.js
index dc91ad2..df01652 100644
--- a/src/pages/profile/RoleSettings.js
+++ b/src/pages/profile/RoleSettings.js
@@ -1,4 +1,5 @@
 import React, { useState, useEffect } from 'react';
+import { useAuth } from 'oidc-react';
 import {
   EuiTitle,
   EuiSelect,
@@ -9,36 +10,36 @@ import {
   EuiSpacer,
   EuiFlexGroup,
 } from '@elastic/eui';
-import { getRoles, sendMail, createUserRequest } from '../../actions/user';
 import { useTranslation } from 'react-i18next';
 import styles from './styles';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 const RoleSettings = ({ userRole }) => {
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [roles, setRoles] = useState([]);
   const [selectedRole, setSelectedRole] = useState(undefined);
 
   useEffect(() => {
-    getUserRoles();
-  }, []);
-
-  const getUserRoles = () => {
-    getRoles().then((rolesResult) => {
-      const rolesArray = [];
-      rolesResult.forEach((role) => {
-        rolesArray.push({ id: role.id, text: role.name });
-      });
-      setRoles(rolesArray);
-    });
-  };
+    const fetchRoles = async () => {
+      const roles = await client.getRoles();
+      const filteredRoles = roles
+        .filter((role) => role.name !== userRole)
+        .map((role) => ({ value: role.name, text: role.name }));
+      setRoles(filteredRoles);
+    };
+    fetchRoles();
+  }, [client]);
 
   const onSendRoleRequest = async () => {
+    const { email, sub } = await getUserInfo();
     if (selectedRole) {
-      const message = `The user ${sessionStorage.getItem('username')} (${sessionStorage.getItem('email')}) has made a request to get the role : ${selectedRole}.`;
-      const result = await createUserRequest(sessionStorage.getItem('kcId'), message);
-      await sendMail('User role request', message);
+      const message = `The user ${email} has made a request to get the role : ${selectedRole}.`;
+      const result = await client.createUserRequest(message, sub);
       if (result.error) {
         toast.error(
           <ToastMessage title={t('validation:error')} message={result.error} />
@@ -74,7 +75,7 @@ const RoleSettings = ({ userRole }) => {
             />
           </EuiFormRow>
           <EuiSpacer size={'l'} />
-          <EuiButton onClick={() => onSendRoleRequest()} fill>
+          <EuiButton onClick={() => onSendRoleRequest()} fill disabled={!selectedRole}>
             {t('common:validationActions.send')}
           </EuiButton>
         </EuiPanel>
diff --git a/src/pages/profile/UserFieldsDisplaySettings.js b/src/pages/profile/UserFieldsDisplaySettings.js
index 2984427..e30e423 100644
--- a/src/pages/profile/UserFieldsDisplaySettings.js
+++ b/src/pages/profile/UserFieldsDisplaySettings.js
@@ -8,22 +8,21 @@ import {
   EuiPanel,
   EuiTitle,
 } from '@elastic/eui';
-import {
-  createUserFieldsDisplaySettings,
-  deleteUserFieldsDisplaySettings,
-  updateUserFieldsDisplaySettings,
-} from '../../actions/user';
 import { Trans, useTranslation } from 'react-i18next';
 import { buildFieldName } from '../../Utils';
 import BulletPointList from '../../components/BulletPointList/BulletPointList';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
 
 /*
   User fields display settings are used to choose which fields are displayed in results table after a search.
   If the user has no settings, the default are used. Default settings are the same for all users, chosen by admin at standard setup.
  */
 const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields }) => {
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
   const { t } = useTranslation(['profile', 'common', 'validation']);
   const [settingsOptions, setSettingsOptions] = useState([]);
   const [selectedOptionsIds, setSelectedOptionsIds] = useState([]);
@@ -88,21 +87,12 @@ const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields
       return;
     }
     let result;
-    if (userSettings) {
-      result = await updateUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId'),
-        selectedOptionsIds
-      );
-    } else {
-      result = await createUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId'),
-        selectedOptionsIds
-      );
-    }
-    setUserSettings(selectedOptionsIds);
-    if (result.error) {
+    const { sub } = await getUserInfo();
+    const response = await client.setUserFieldsDisplaySettings(sub, selectedOptionsIds);
+    if (response && response.length == 0) {
       toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
     } else {
+      setUserSettings(selectedOptionsIds);
       toast.success(t('profile:fieldsDisplaySettings.updatedSettingsSuccess'));
     }
   };
@@ -126,21 +116,16 @@ const UserFieldsDisplaySettings = ({ userSettings, setUserSettings, publicFields
   // With current logic, no user settings means it should use default.
   // So in this case 'reset' means delete current user settings.
   const onSettingsReset = async () => {
-    // TODO add a confirmation modal ?
-    const result = await deleteUserFieldsDisplaySettings(
-      sessionStorage.getItem('userId')
-    );
-    if (result.error) {
-      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
-    } else {
-      setUserSettings(null);
-      toast.success(
-        <ToastMessage
-          title={t('profile:fieldsDisplaySettings.deleteSettingsSuccess')}
-          message={t('profile:fieldsDisplaySettings.deleteSettingsSuccessDefault')}
-        />
-      );
+    if (!settingsOptions) {
+      toast.error(t('profile:fieldsDisplaySettings.selectionResetFailure'));
+      return;
     }
+    const newSettingsOptions = [];
+    settingsOptions.forEach((option) => {
+      option.checked = false;
+      newSettingsOptions.push(option);
+    });
+    setSettingsOptions(newSettingsOptions);
   };
 
   const SelectableSettingsPanel = () => {
diff --git a/src/pages/results/ResultsTableMUI.js b/src/pages/results/ResultsTableMUI.js
index 0fd0c86..7586c59 100644
--- a/src/pages/results/ResultsTableMUI.js
+++ b/src/pages/results/ResultsTableMUI.js
@@ -1,11 +1,11 @@
-import React, { useEffect, useMemo, useState } from 'react';
+import React, { useEffect, useState } from 'react';
 import { Trans, useTranslation } from 'react-i18next';
 import MUIDataTable from 'mui-datatables';
 import { createTheme, ThemeProvider } from '@mui/material';
-import { makeStyles } from '@mui/styles';
-import { fetchPublicFields } from '../../actions/source';
-import { fetchUserFieldsDisplaySettings } from '../../actions/user';
 import { buildFieldName } from '../../Utils';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
+import { useUserInfo } from '../../contexts/TokenContext';
+import { makeStyles } from '@mui/styles';
 import { useEuiTheme, transparentize, EuiTitle } from '@elastic/eui';
 
 const useStyles = makeStyles(() => ({
@@ -27,52 +27,55 @@ const ResultsTableMUI = ({
   setResourceFlyoutDataFromId,
 }) => {
   const { t } = useTranslation('results');
+  const client = useGatekeeper();
+  const getUserInfo = useUserInfo();
   const { euiTheme } = useEuiTheme();
   const classes = useStyles();
   const [publicFields, setPublicFields] = useState([]);
-  const [userFieldsIds, setUserFieldsIds] = useState([]);
   const [isLoading, setIsLoading] = useState(true);
   const [rowsPerPage, setRowsPerPage] = useState(15);
+  const [rows, setRows] = useState([]);
+  const [columns, setColumns] = useState([]);
 
-  // Fetch public fields and user display settings
   useEffect(() => {
-    const fetchData = async () => {
-      setIsLoading(true);
-      const publicFieldsResults = await fetchPublicFields();
-      setPublicFields(publicFieldsResults);
-      const userStdFieldsIds = await fetchUserFieldsDisplaySettings(
-        sessionStorage.getItem('userId')
-      );
-      const defaultStdFieldsIds = [1, 20, 36, 9, 31, 13];
-      // TODO replace hard-coded array by gatekeeper fetch on default settings
-      // If no userStdFields, use system default ones.
-      setUserFieldsIds(userStdFieldsIds || defaultStdFieldsIds);
+    const fetchFieldsForUser = async () => {
+      const publicFields = await client.getPublicFields(); // TODO: get also private fields that user has access to
+      setPublicFields(publicFields);
+    };
+    const getUserFieldsDisplaySettings = async () => {
+      const userInfo = await getUserInfo();
+      const userFields = await client.getUserFieldsDisplaySettings(userInfo.sub);
+      if (userFields && userFields.length > 0) {
+        const columns = buildColumns(userFields);
+        setColumns(columns);
+        const rows = buildRows(searchResults, columns);
+        setRows(rows);
+        setIsLoading(false);
+      }
     };
-    fetchData();
+    if (!searchResults || searchResults.length === 0 || searchResults.error) {
+      return;
+    }
+    fetchFieldsForUser();
+    getUserFieldsDisplaySettings();
   }, [searchResults]);
 
-  // Memoize columns
-  const columns = useMemo(() => {
-    if (publicFields.length === 0) {
-      return [];
-    }
-    let dataColumns = [
+  const buildColumns = (userFields) => {
+    let columns = [
       {
         name: 'id',
         label: 'ID',
         options: { display: 'excluded' },
       },
     ];
-    publicFields.forEach((publicField) => {
-      dataColumns.push({
-        name: publicField.field_name,
-        label: buildFieldName(publicField.field_name),
+    // Add to columns the standard fields given as parameter
+    // Retrieve
+    userFields.forEach((field) => {
+      columns.push({
+        name: field.field_name,
+        label: buildFieldName(field.field_name),
         options: {
-          display: userFieldsIds.includes(publicField.id),
-          // Apply styling on columns headers
-          setCellHeaderProps: () => ({
-            className: classes.centeredHeader,
-          }),
+          display: true,
           // Apply styling on columns headers text
           customHeadLabelRender: (columnMeta) => {
             return (
@@ -83,7 +86,7 @@ const ResultsTableMUI = ({
           },
           // Truncate text to avoid oversize rows
           customBodyRenderLite: (dataIndex, rowIndex) => {
-            let value = rows[rowIndex][publicField.field_name];
+            let value = rows[rowIndex][field.field_name];
             if (value && value.length >= 150) {
               value = value.substring(0, 150) + ' ...';
             }
@@ -93,15 +96,26 @@ const ResultsTableMUI = ({
           setCellProps: () => ({
             style: {
               maxWidth: '350px',
+              textOverflow: 'ellipsis',
             },
           }),
         },
       });
     });
-    // sort columns alphabetically for clarity
-    dataColumns.sort((a, b) => (a.name > b.name ? 1 : -1));
-    return dataColumns;
-  }, [publicFields, userFieldsIds]);
+    columns.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
+    return columns;
+  };
+
+  const buildRows = (results, columns) => {
+    return results.map((result) => {
+      let row = { id: result.id };
+      columns.forEach((column) => {
+        const value = getValueByPath(result, column.name);
+        row[column.name] = typeof value === 'string' ? value : value?.toString();
+      });
+      return row;
+    });
+  };
 
   // Returns value from JSON obj associated to key string.
   const getValueByPath = (obj, path) => {
@@ -115,49 +129,9 @@ const ResultsTableMUI = ({
     }, obj);
   };
 
-  const rows = useMemo(() => {
-    const buildRows = (results, columns) => {
-      if (results.length === 0) {
-        return [];
-      }
-      return results.map((result) => {
-        let row = { id: result.id };
-        columns.forEach((column) => {
-          const value = getValueByPath(result, column.name);
-          row[column.name] = typeof value === 'string' ? value : value?.toString();
-        });
-        return row;
-      });
-    };
-    const rows = buildRows(searchResults, columns);
-    setIsLoading(false);
-    return searchResults && columns.length > 0 ? rows : [];
-  }, [searchResults, columns]);
-
-  const getRowIdFromResourceData = (id) => {
-    if (!rows || rows.length === 0) {
-      return -1;
-    }
-    for (let index = 0; index < rows.length; index++) {
-      if (rows[index].id === id) {
-        return index;
-      }
-    }
-    return -1;
-  };
-
-  // On page load, check table rows from selected resources from map
-  const selectedRows = useMemo(() => {
-    return selectedRowsIds.map((id) => getRowIdFromResourceData(id));
-  }, [rows, selectedRowsIds]);
-
   // Add row to list of selected on checkbox click
-  const onRowSelectionCallback = (selectedRow, allSelectedRows) => {
-    setSelectedRowsIds(
-      allSelectedRows.map((row) => {
-        return rows[row.dataIndex].id;
-      })
-    );
+  const onRowSelectionCallback = (currentRowsSelected, allRowsSelected, rowsSelected) => {
+    setSelectedRowsIds(rowsSelected.map((index) => rows[index].id));
   };
 
   // Open resource flyout on row click (any cell)
@@ -242,7 +216,9 @@ const ResultsTableMUI = ({
     },
     selectableRows: 'multiple',
     selectableRowsOnClick: false,
-    rowsSelected: selectedRows,
+    rowsSelected: rows
+      .filter((row) => selectedRowsIds.includes(row.id))
+      .map((row) => rows.indexOf(row)),
     rowsPerPage: rowsPerPage,
     onChangeRowsPerPage: (newRowsPerPage) => {
       setRowsPerPage(newRowsPerPage);
@@ -250,7 +226,7 @@ const ResultsTableMUI = ({
     rowsPerPageOptions: [15, 30, 50, 100, 250],
     jumpToPage: true,
     searchPlaceholder: t('results:table.search'),
-    elevation: 0, // remove the boxShadow style
+    elevation: 0,
     customToolbarSelect: () => <CustomSelectToolbar />,
     selectToolbarPlacement: 'above',
     onRowSelectionChange: onRowSelectionCallback,
diff --git a/src/pages/search/AdvancedSearch/AdvancedSearch.js b/src/pages/search/AdvancedSearch/AdvancedSearch.js
index 53f5ad4..29625ef 100644
--- a/src/pages/search/AdvancedSearch/AdvancedSearch.js
+++ b/src/pages/search/AdvancedSearch/AdvancedSearch.js
@@ -32,7 +32,6 @@ import {
 import React, { Fragment, useEffect, useState } from 'react';
 import {
   changeNameToLabel,
-  createAdvancedQueriesBySource,
   getFieldsBySection,
   getSections,
   removeArrayElement,
@@ -40,15 +39,16 @@ import {
   updateArrayElement,
   updateSearchFieldValues,
 } from '../../../Utils';
-import { getQueryCount, searchQuery } from '../../../actions/source';
 import { DateOptions, NumericOptions, Operators } from '../Data';
-import { addUserHistory, fetchUserHistory } from '../../../actions/user';
 import { useTranslation } from 'react-i18next';
 import styles from './styles.js';
 import moment from 'moment';
 import SearchModeSwitcher from '../SearchModeSwitcher';
 import { toast } from 'react-toastify';
 import ToastMessage from '../../../components/ToastMessage/ToastMessage';
+import { useGatekeeper } from '../../../contexts/GatekeeperContext';
+import { useAuth } from 'oidc-react';
+import { buildDslQuery } from '../../../Utils';
 
 const updateSources = (
   searchFields,
@@ -69,7 +69,7 @@ const updateSources = (
         availableSources = updatedSources;
       }
       updatedSources = [];
-      field.sources.forEach((sourceId) => {
+      field.sources?.forEach((sourceId) => {
         noPrivateField = false;
         const source = availableSources.find((src) => src.id === sourceId);
         if (source && !updatedSources.includes(source)) {
@@ -130,18 +130,6 @@ const fieldValuesToString = (field) => {
   return strValues;
 };
 
-const fetchHistory = (setUserHistory) => {
-  fetchUserHistory(sessionStorage.getItem('kcId')).then((result) => {
-    if (result[0] && result[0].ui_structure) {
-      result.forEach((item) => {
-        item.ui_structure = JSON.parse(item.ui_structure);
-        item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
-      });
-    }
-    setUserHistory(result);
-  });
-};
-
 const updateSearch = (setSearch, searchFields, selectedOperatorId, setSearchCount) => {
   let searchText = '';
   searchFields.forEach((field) => {
@@ -172,12 +160,27 @@ const HistorySelect = ({
   setUserHistory,
 }) => {
   const { t } = useTranslation('search');
+  const auth = useAuth();
+  const client = useGatekeeper();
   const [historySelectError, setHistorySelectError] = useState(undefined);
   const [selectedSavedSearch, setSelectedSavedSearch] = useState(undefined);
 
   useEffect(() => {
-    fetchHistory(setUserHistory);
-  }, [setUserHistory]);
+    const fetchHistory = async (sub) => {
+      const userHistory = await client.getUserHistory(sub);
+      if (userHistory[0] && userHistory[0].ui_structure) {
+        userHistory.forEach((item) => {
+          item.ui_structure = JSON.parse(item.ui_structure);
+          item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
+        });
+        setUserHistory(userHistory);
+      }
+    };
+    const sub = auth.userData?.profile?.sub;
+    if (sub) {
+      fetchHistory(sub);
+    }
+  }, [auth.userData]);
 
   const onHistoryChange = (selectedSavedSearch) => {
     setHistorySelectError(undefined);
@@ -262,69 +265,82 @@ const SearchBar = ({
   const [userHistory, setUserHistory] = useState({});
   const [isSaveSearchModalOpen, setIsSaveSearchModalOpen] = useState(false);
   const [readOnlyQuery, setReadOnlyQuery] = useState(true);
+  const auth = useAuth();
+  const client = useGatekeeper();
 
   const closeSaveSearchModal = () => {
     setIsSaveSearchModalOpen(false);
   };
 
-  const onClickAdvancedSearch = () => {
+  const onClickAdvancedSearch = async () => {
     if (search.trim()) {
       setIsLoading(true);
-      const queriesWithIndices = createAdvancedQueriesBySource(
-        standardFields,
-        search,
-        selectedSources,
-        availableSources
-      );
-      searchQuery(queriesWithIndices).then((result) => {
-        setSearchResults(result);
-        setSelectedTabNumber(1);
-        if (isLoading) {
-          setIsLoading(false);
-        }
-      });
+      const advancedQuery = buildDslQuery(search, standardFields);
+      const params = {
+        query: advancedQuery,
+        sourcesId: availableSources.map((source) => source.id),
+        fieldsId: standardFields,
+        advancedQuery: true,
+      };
+      const result = await client.searchQuery(params);
+      setSearchResults(result);
+      setSelectedTabNumber(1);
+      if (isLoading) {
+        setIsLoading(false);
+      }
     }
   };
 
-  const onClickCountResults = () => {
+  const onClickCountResults = async () => {
     if (!!search) {
-      const queriesWithIndices = createAdvancedQueriesBySource(
-        standardFields,
-        search,
-        selectedSources,
-        availableSources
-      );
-      getQueryCount(queriesWithIndices).then((result) => {
-        if (result || result === 0) {
-          setSearchCount(result);
-        }
-      });
+      const query = buildDslQuery(search, standardFields);
+      const params = {
+        query,
+        sourcesId: availableSources.map((source) => source.id),
+        fieldsId: standardFields,
+        advancedQuery: true,
+      };
+      const result = await client.searchQuery(params);
+      if (!result || result.error) {
+        toast.error(
+          <ToastMessage title={t('validation:error')} message={result.error} />
+        );
+        setSearchCount(0);
+      }
+      setSearchCount(result.length);
     }
   };
 
-  const addHistory = (
+  const addHistory = async (
     search,
     searchName,
     searchFields,
     searchDescription,
     setUserHistory
   ) => {
-    addUserHistory(
-      sessionStorage.getItem('kcId'),
-      search,
-      searchName,
-      searchFields,
-      searchDescription
-    ).then((result) => {
-      if (result.error) {
-        toast.error(
-          <ToastMessage title={t('validation:error')} message={result.error} />
-        );
-      } else {
-        toast.success(t('search:advancedSearch.searchHistory.searchSaved'));
+    const sub = auth.userData?.profile?.sub;
+    if (!sub) {
+      return;
+    }
+    const params = {
+      name: searchName,
+      query: search,
+      ui_structure: JSON.stringify(searchFields),
+      description: searchDescription,
+    };
+    const result = await client.addHistory(sub, params);
+    if (result.error) {
+      toast.error(<ToastMessage title={t('validation:error')} message={result.error} />);
+    } else {
+      const userHistory = await client.getUserHistory(sub);
+      if (userHistory[0] && userHistory[0].ui_structure) {
+        userHistory.forEach((item) => {
+          item.ui_structure = JSON.parse(item.ui_structure);
+          item.label = `${item.name} - ${new Date(item.createdat).toLocaleString()}`;
+        });
+        setUserHistory(userHistory);
       }
-      fetchHistory(setUserHistory);
-    });
+    }
   };
 
   const SaveSearchModal = () => {
@@ -544,26 +560,9 @@ const PopoverSelect = ({ standardFields, searchFields, setSearchFields }) => {
     }
   };
 
-  const SelectField = () => {
-    const renderOption = (option, searchValue, contentClassName) => {
-      const { label, color } = option;
-      return <EuiHealth color={color}>{label}</EuiHealth>;
-    };
-    if (selectedSection.length) {
-      return (
-        <>
-          <EuiComboBox
-            placeholder={t('search:advancedSearch.fields.addFieldPopover.selectSection')}
-            singleSelection={{ asPlainText: true }}
-            options={getFieldsBySection(standardFields, selectedSection[0])}
-            selectedOptions={selectedField}
-            onChange={(selected) => setSelectedField(selected)}
-            isClearable={true}
-            renderOption={renderOption}
-          />
-        </>
-      );
-    }
+  const renderOption = (option, searchValue, contentClassName) => {
+    const { label, color } = option;
+    return <EuiHealth color={color}>{label}</EuiHealth>;
   };
 
   return (
@@ -596,7 +595,18 @@ const PopoverSelect = ({ standardFields, searchFields, setSearchFields }) => {
           }}
           isClearable={false}
         />
-        <SelectField />
+        <EuiComboBox
+          placeholder={t('search:advancedSearch.fields.addFieldPopover.selectSection')}
+          singleSelection={{ asPlainText: true }}
+          options={getFieldsBySection(standardFields, selectedSection[0])}
+          selectedOptions={selectedField}
+          onChange={(selected) => {
+            setSelectedField(selected);
+          }}
+          isClearable={true}
+          renderOption={renderOption}
+          isDisabled={selectedSection.length === 0}
+        />
       </EuiFlexGroup>
       <EuiPopoverFooter style={styles.noBorder} paddingSize={'m'}>
         <EuiButton
@@ -683,7 +693,7 @@ const PopoverValueContent = ({
     setSearchFields(updatedSearchFields);
     updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
     setFieldCount(updateArrayElement(fieldCount, index));
-    if (searchFields[index].sources.length) {
+    if (searchFields[index].sources?.length) {
       const filteredSources = [];
       searchFields[index].sources.forEach((sourceId) => {
         let source;
@@ -1098,13 +1108,19 @@ const PopoverValueButton = ({
   const { t } = useTranslation('search');
   const [isPopoverValueOpen, setIsPopoverValueOpen] = useState(false);
 
+  const handleButtonClick = (e) => {
+    e.preventDefault();
+    e.stopPropagation();
+    setIsPopoverValueOpen((prevState) => !prevState);
+  };
+
   return (
     <EuiPopover
       panelPaddingSize="s"
       button={
         <EuiButtonIcon
           size="s"
-          onClick={() => setIsPopoverValueOpen(!isPopoverValueOpen)}
+          onClick={handleButtonClick}
           iconType="documentEdit"
           title={t('search:advancedSearch.fields.fieldContentPopover.addFieldValues')}
           aria-label={t(
@@ -1154,19 +1170,20 @@ const FieldsPanel = ({
   sources,
 }) => {
   const { t } = useTranslation('search');
+  const client = useGatekeeper();
 
-  const countFieldValues = (field, index) => {
+  const countFieldValues = async (field, index) => {
     const fieldStr = `{${fieldValuesToString(field)}}`;
-    const queriesWithIndices = createAdvancedQueriesBySource(
-      standardFields,
-      fieldStr,
-      selectedSources,
-      availableSources
-    );
-    getQueryCount(queriesWithIndices).then((result) => {
-      if (result || result === 0)
-        setFieldCount(updateArrayElement(fieldCount, index, result));
-    });
+    const advancedQuery = buildDslQuery(fieldStr, standardFields);
+    const params = {
+      query: advancedQuery,
+      sourcesId: availableSources.map((source) => source.id),
+      fieldsId: standardFields,
+      advancedQuery: true,
+    };
+    const result = await client.searchQuery(params);
+    const fieldCountUpdated = updateArrayElement(fieldCount, index, result.length);
+    setFieldCount(fieldCountUpdated);
   };
 
   const handleRemoveField = (index) => {
@@ -1224,7 +1241,7 @@ const FieldsPanel = ({
     updateSearch(setSearch, updatedSearchFields, selectedOperatorId, setSearchCount);
   };
 
-  if (standardFields === []) {
+  if (standardFields == []) {
     return <h2>{t('search:advancedSearch.fields.loadingFields')}</h2>;
   }
 
@@ -1256,7 +1273,7 @@ const FieldsPanel = ({
                 <EuiFlexItem>
                   {field.isValidated ? (
                     <>
-                      {field.sources.length ? (
+                      {field.sources?.length ? (
                         <EuiHealth color="danger">
                           {fieldValuesToString(field).replace(/_|\./g, ' ')}
                         </EuiHealth>
@@ -1268,7 +1285,7 @@ const FieldsPanel = ({
                     </>
                   ) : (
                     <>
-                      {field.sources.length ? (
+                      {field.sources?.length ? (
                         <EuiHealth color="danger">
                           {field.name.replace(/_|\./g, ' ')}
                         </EuiHealth>
diff --git a/src/pages/search/BasicSearch/BasicSearch.js b/src/pages/search/BasicSearch/BasicSearch.js
index dfdd1f1..77f208a 100644
--- a/src/pages/search/BasicSearch/BasicSearch.js
+++ b/src/pages/search/BasicSearch/BasicSearch.js
@@ -6,15 +6,13 @@ import {
   EuiFlexItem,
   EuiSpacer,
 } from '@elastic/eui';
-import { createBasicQueriesBySource } from '../../../Utils';
-import { searchQuery } from '../../../actions/source';
 import { useTranslation } from 'react-i18next';
 import SearchModeSwitcher from '../SearchModeSwitcher';
+import { useGatekeeper } from '../../../contexts/GatekeeperContext';
 
 const BasicSearch = ({
   standardFields,
   availableSources,
-  selectedSources,
   basicSearch,
   setBasicSearch,
   setIsAdvancedSearch,
@@ -23,24 +21,20 @@ const BasicSearch = ({
   setSelectedTabNumber,
 }) => {
   const { t } = useTranslation('search');
+  const client = useGatekeeper();
   const [isLoading, setIsLoading] = useState(false);
 
-  const onFormSubmit = (e) => {
+  const onFormSubmit = async (e) => {
     e.preventDefault();
     setIsLoading(true);
-    const queriesWithIndices = createBasicQueriesBySource(
-      standardFields,
-      basicSearch,
-      selectedSources,
-      availableSources
-    );
-    searchQuery(queriesWithIndices).then((result) => {
-      setSearchResults(result);
-      if (isLoading) {
-        setIsLoading(false);
-      }
-      setSelectedTabNumber(1);
+    const result = await client.searchQuery({
+      query: basicSearch,
+      sourcesId: availableSources.map((source) => source.id),
+      fieldsId: standardFields,
     });
+    setIsLoading(false);
+    setSearchResults(result);
+    setSelectedTabNumber(1);
   };
 
   return (
diff --git a/src/pages/search/Search.js b/src/pages/search/Search.js
index 6f31278..2afd2c3 100644
--- a/src/pages/search/Search.js
+++ b/src/pages/search/Search.js
@@ -2,18 +2,14 @@ import React, { useState, useEffect } from 'react';
 import { EuiTabbedContent, EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
 import Results from '../results/Results';
 import SearchMap from '../maps/SearchMap';
-import { removeNullFields } from '../../Utils.js';
-import {
-  fetchPublicFields,
-  fetchUserPolicyFields,
-  fetchSources,
-} from '../../actions/source';
 import { useTranslation } from 'react-i18next';
 import AdvancedSearch from './AdvancedSearch/AdvancedSearch';
 import BasicSearch from './BasicSearch/BasicSearch';
+import { useGatekeeper } from '../../contexts/GatekeeperContext';
 import ResourceFlyout from '../results/ResourceFlyout/ResourceFlyout';
 
 const Search = () => {
+  const client = useGatekeeper();
   const { t } = useTranslation('search');
   const [selectedTabNumber, setSelectedTabNumber] = useState(0);
   const [isAdvancedSearch, setIsAdvancedSearch] = useState(false);
@@ -29,37 +25,59 @@ const Search = () => {
   const [resourceFlyoutData, setResourceFlyoutData] = useState({});
 
   useEffect(() => {
-    fetchPublicFields().then((resultStdFields) => {
-      resultStdFields.forEach((field) => {
-        field.sources = [];
-      });
-      setStandardFields(resultStdFields);
-      fetchUserPolicyFields(sessionStorage.getItem('kcId')).then((resultPolicyFields) => {
-        const userFields = resultStdFields;
-        resultPolicyFields.forEach((polField) => {
-          const stdFieldIndex = userFields.findIndex(
-            (stdField) => stdField.id === polField.std_id
-          );
-          if (stdFieldIndex >= 0) {
-            if (!userFields[stdFieldIndex].sources.includes(polField.source_id))
-              userFields[stdFieldIndex].sources.push(polField.source_id);
-          } else {
-            const newField = {
-              id: polField.std_id,
-              sources: [polField.source_id],
-              ...polField,
-            };
-            userFields.push(newField);
-          }
-        });
-        userFields.sort((a, b) => (a.id > b.id ? 1 : b.id > a.id ? -1 : 0));
-        setStandardFields(removeNullFields(userFields));
-      });
-    });
-    fetchSources(sessionStorage.getItem('kcId')).then((result) => {
-      setSources(result);
-      setAvailableSources(result);
-    });
+    if (!client) {
+      return;
+    }
+    const fetchElements = async () => {
+      const publicFields = await client.getPublicFields();
+      setStandardFields(publicFields);
+      const sources = await client.getSources();
+      setSources(sources);
+      setAvailableSources(sources);
+    };
+    fetchElements();
+    // client
+    //   .getPublicFields()
+    //   .then((resultStdFields) => {
+    //     resultStdFields.forEach((field) => {
+    //       field.sources = [];
+    //     });
+    //     setStandardFields(resultStdFields);
+    //     fetchUserPolicyFields(sessionStorage.getItem('kcId')).then(
+    //       (resultPolicyFields) => {
+    //         const userFields = resultStdFields;
+    //         resultPolicyFields.forEach((polField) => {
+    //           const stdFieldIndex = userFields.findIndex(
+    //             (stdField) => stdField.id === polField.std_id
+    //           );
+    //           if (stdFieldIndex >= 0) {
+    //             if (!userFields[stdFieldIndex].sources.includes(polField.source_id))
+    //               userFields[stdFieldIndex].sources.push(polField.source_id);
+    //           } else {
+    //             const newField = {
+    //               id: polField.std_id,
+    //               sources: [polField.source_id],
+    //               ...polField,
+    //             };
+    //             userFields.push(newField);
+    //           }
+    //         });
+    //         userFields.sort((a, b) => (a.id > b.id ? 1 : b.id > a.id ? -1 : 0));
+    //         setStandardFields(removeNullFields(userFields));
+    //       }
+    //     );
+    //   })
+    //   .catch((error) => {
+    //     console.error(error);
+    //   });
+    // fetchSources(sessionStorage.getItem('kcId'))
+    //   .then((result) => {
+    //     setSources(result);
+    //     setAvailableSources(result);
+    //   })
+    //   .catch((error) => {
+    //     console.error(error);
+    //   });
   }, []);
 
   // On new search, reset selected rows
@@ -109,7 +127,6 @@ const Search = () => {
                 setIsAdvancedSearch={setIsAdvancedSearch}
                 standardFields={standardFields}
                 availableSources={availableSources}
-                selectedSources={selectedSources}
                 basicSearch={basicSearch}
                 setBasicSearch={setBasicSearch}
                 setSearchResults={setSearchResults}
diff --git a/src/services/GatekeeperService.js b/src/services/GatekeeperService.js
new file mode 100644
index 0000000..498864a
--- /dev/null
+++ b/src/services/GatekeeperService.js
@@ -0,0 +1,148 @@
+export class InSylvaGatekeeperClient {
+  constructor(getUserInfo) {
+    this.getUserInfo = getUserInfo;
+  }
+  async get(path, payload) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'GET',
+        headers,
+        mode: 'cors',
+        body: JSON.stringify(payload),
+      }
+    );
+    return await response.json();
+  }
+
+  async post(path, requestContent) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'POST',
+        headers,
+        body: JSON.stringify(requestContent),
+        mode: 'cors',
+      }
+    );
+    return await response.json();
+  }
+
+  async delete(path, requestContent) {
+    const userInfo = await this.getUserInfo();
+    const { access_token } = userInfo;
+    const headers = {
+      'Content-Type': 'application/json',
+      Authorization: `Bearer ${access_token}`,
+    };
+    const response = await fetch(
+      `${process.env.REACT_APP_IN_SYLVA_GATEKEEPER_BASE_URL}${path}`,
+      {
+        method: 'DELETE',
+        headers,
+        body: JSON.stringify(requestContent),
+        mode: 'cors',
+      }
+    );
+    if (response.status === 204) {
+      return {
+        success: true,
+        message: 'Request deleted successfully',
+      };
+    } else {
+      return {
+        success: false,
+        message: 'Request not deleted',
+      };
+    }
+  }
+
+  async createUser(sub, email) {
+    const path = `/users`;
+    return await this.post(path, {
+      kc_id: sub,
+      email,
+    });
+  }
+
+  async findUserBySub(sub) {
+    const path = `/users/${sub}`;
+    return await this.get(path);
+  }
+
+  async getRoles() {
+    const path = `/roles`;
+    return await this.get(path);
+  }
+
+  async createUserRequest(message, sub) {
+    const path = `/users/${sub}/requests`;
+    return await this.post(path, {
+      message: message,
+    });
+  }
+
+  async deleteUserRequest(id) {
+    const path = `/user-requests/${id}`;
+    return await this.delete(path, {});
+  }
+
+  async getGroups() {
+    const path = `/groups`;
+    return await this.get(path);
+  }
+
+  async getPublicFields() {
+    const path = `/public_std_fields`;
+    return await this.get(path);
+  }
+
+  async getUserFieldsDisplaySettings(sub) {
+    const path = `/users/${sub}/fields`;
+    return await this.get(path);
+  }
+
+  async setUserFieldsDisplaySettings(sub, fields) {
+    const path = `/users/${sub}/fields`;
+    return await this.post(path, {
+      fields_id: fields,
+    });
+  }
+
+  async getSources() {
+    const path = `/sources`;
+    return await this.get(path);
+  }
+
+  async getUserSources(sub) {
+    const path = `/users/${sub}/sources`;
+    return await this.get(path);
+  }
+
+  async searchQuery(payload) {
+    const path = `/search`;
+    return await this.post(path, payload);
+  }
+
+  async getUserHistory(sub) {
+    const path = `/users/${sub}/history`;
+    return await this.get(path);
+  }
+
+  async addHistory(sub, payload) {
+    const path = `/users/${sub}/history`;
+    return await this.post(path, payload);
+  }
+}
diff --git a/src/services/TokenService.js b/src/services/TokenService.js
new file mode 100644
index 0000000..4ead043
--- /dev/null
+++ b/src/services/TokenService.js
@@ -0,0 +1,38 @@
+export default class TokenService {
+  async getUserInfo(access_token) {
+    const issuerUrl = process.env.REACT_APP_KEYCLOAK_BASE_URL;
+    const userInfoEndpoint = issuerUrl + '/protocol/openid-connect/userinfo';
+
+    const response = await fetch(userInfoEndpoint, {
+      headers: {
+        Authorization: `Bearer ${access_token}`,
+      },
+    });
+    if (response.status !== 200) {
+      return null;
+    } else {
+      const userInfo = await response.json();
+      return userInfo;
+    }
+  }
+
+  async refreshToken(refresh_token) {
+    const issuerUrl = process.env.REACT_APP_KEYCLOAK_BASE_URL;
+    const tokenEndpoint = issuerUrl + '/protocol/openid-connect/token';
+
+    const response = await fetch(tokenEndpoint, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/x-www-form-urlencoded',
+      },
+      body: `grant_type=refresh_token&refresh_token=${refresh_token}&client_id=${process.env.REACT_APP_KEYCLOAK_CLIENT_ID}&client_secret=${process.env.REACT_APP_KEYCLOAK_CLIENT_SECRET}`,
+    });
+
+    if (response.status !== 200) {
+      return null;
+    } else {
+      const response = await response.json();
+      return response;
+    }
+  }
+}
-- 
GitLab