From 495e1d948227a0a3b4adbaedba38f997418d638e Mon Sep 17 00:00:00 2001
From: Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
Date: Wed, 1 Jul 2020 15:42:59 +0300
Subject: [PATCH] cleaned-up authentication and reduced code bloat

Change-Id: Id41e329bc44a9bdf1266ca0402b655186fc02856
---
 .../jami/jams/authmodule/TokenController.java |  3 +-
 .../net/jami/datastore/main/DataStore.java    |  7 +-
 .../jams/common/objects/user/UserProfile.java |  2 -
 .../api/admin/devices/DeviceServlet.java      |  5 +
 .../api/admin/devices/DevicesServlet.java     |  3 +
 .../directory/DirectoryEntryServlet.java      | 76 +++++++--------
 .../api/admin/update/SubscriptionServlet.java |  4 +
 .../api/admin/update/UpdateServlet.java       |  4 +
 .../servlets/api/admin/users/UserServlet.java |  6 ++
 .../api/admin/users/UsersServlet.java         |  3 +
 .../api/auth/device/DeviceServlet.java        |  4 +-
 .../directory/SearchDirectoryServlet.java     |  1 -
 .../servlets/api/auth/user/UserServlet.java   |  4 -
 .../api/install/CreateAuthSourceServlet.java  |  4 +
 .../servlets/api/install/CreateCAServlet.java |  4 +
 .../install/CreateServerSettingsServlet.java  |  4 +
 .../api/install/InstallLastStepServlet.java   |  3 +
 .../api/install/StartInstallServlet.java      |  5 +-
 .../servlets/filters/AdminApiFilter.java      | 86 -----------------
 .../server/servlets/filters/ApiFilter.java    | 53 ++--------
 .../servlets/filters/AuthRequestType.java     |  8 ++
 .../server/servlets/filters/FilterUtils.java  | 96 +++++++++++++++++++
 .../servlets/filters/InstallFilter.java       | 21 +---
 .../server/servlets/filters/JWTValidator.java | 50 ----------
 .../src/main/resources/webapp/js/api.js       |  2 +-
 .../src/main/resources/webapp/js/user.js      |  6 +-
 ldap-connector/pom.xml                        | 17 ++++
 .../src/test/java/tests/GenericLDAPTest.java  | 61 ++++++++++++
 .../src/test/resources/bootstrap.ldif         | 45 +++++++++
 .../src/test/resources/ldapconfig.json        | 20 ++++
 30 files changed, 350 insertions(+), 257 deletions(-)
 delete mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java
 create mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java
 create mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java
 delete mode 100644 jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java
 create mode 100644 ldap-connector/src/test/java/tests/GenericLDAPTest.java
 create mode 100644 ldap-connector/src/test/resources/bootstrap.ldif
 create mode 100644 ldap-connector/src/test/resources/ldapconfig.json

diff --git a/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java b/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java
index 6b2ee493..8f74a124 100644
--- a/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java
+++ b/authentication-module/src/main/java/net/jami/jams/authmodule/TokenController.java
@@ -60,9 +60,11 @@ public class TokenController{
         if(deviceId != null){
             builder.claim("scope",DEVICE);
             builder.claim("deviceId",deviceId);
+            authTokenResponse.setScope(DEVICE);
         }
         else{
             builder.claim("scope",user.getAccessLevel());
+            authTokenResponse.setScope(user.getAccessLevel());
         }
         JWTClaimsSet jwtClaims =  builder.build();
         SignedJWT signedJWT = new SignedJWT(jwsHeader, jwtClaims);
@@ -71,7 +73,6 @@ public class TokenController{
             UserAuthenticationModule.datastore.getJwtDao().storeObject(signedJWT);
             authTokenResponse.setAccess_token(signedJWT.serialize());
             authTokenResponse.setExpires_in(30*60*1000L);
-            authTokenResponse.setScope(user.getAccessLevel());
             authTokenResponse.setToken_type("Bearer");
             return authTokenResponse;
         }
diff --git a/datastore/src/main/java/net/jami/datastore/main/DataStore.java b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
index 4579b802..508711af 100644
--- a/datastore/src/main/java/net/jami/datastore/main/DataStore.java
+++ b/datastore/src/main/java/net/jami/datastore/main/DataStore.java
@@ -24,7 +24,12 @@ package net.jami.datastore.main;
 
 import lombok.Getter;
 import lombok.Setter;
-import net.jami.datastore.dao.*;
+import net.jami.datastore.dao.ContactDao;
+import net.jami.datastore.dao.DeviceDao;
+import net.jami.datastore.dao.JwtDao;
+import net.jami.datastore.dao.SystemDao;
+import net.jami.datastore.dao.UserDao;
+import net.jami.datastore.dao.UserProfileDao;
 import net.jami.jams.common.authentication.AuthenticationSource;
 import net.jami.jams.common.authentication.AuthenticationSourceInfo;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
index c82f15bc..ff274084 100644
--- a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
+++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java
@@ -35,9 +35,7 @@ import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.NoArgsConstructor;
 import lombok.Setter;
-import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.serialization.database.DatabaseObject;
-import net.jami.jams.common.utils.X509Utils;
 import org.bouncycastle.util.encoders.Base64;
 
 import java.lang.reflect.Method;
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java
index ef068abb..d12c92f5 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DeviceServlet.java
@@ -28,9 +28,11 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.responses.DeviceRevocationResponse;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.server.core.workflows.RevokeDeviceFlow;
 
 import java.io.IOException;
@@ -42,6 +44,7 @@ public class DeviceServlet extends HttpServlet {
 
     //Get a detailed device info.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String username = req.getParameter("username");
         String deviceId = req.getParameter("deviceId");
@@ -55,6 +58,7 @@ public class DeviceServlet extends HttpServlet {
 
     //Update device data.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String username = req.getParameter("username");
         String deviceId = req.getParameter("deviceId");
@@ -73,6 +77,7 @@ public class DeviceServlet extends HttpServlet {
 
     //Revoke/delete a device.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getParameter("username").toString(),req.getParameter("deviceId"));
         if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes());
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java
index 05dca792..161a9e75 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/devices/DevicesServlet.java
@@ -28,8 +28,10 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.AccessLevel;
 
 import java.io.IOException;
 
@@ -40,6 +42,7 @@ public class DevicesServlet extends HttpServlet {
 
     //Get a list of devices for a user.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         String username = req.getParameter("username");
         StatementList statementList = new StatementList();
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java
index 8aa3c8e3..3ccddf07 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/directory/DirectoryEntryServlet.java
@@ -1,25 +1,26 @@
 /*
-* Copyright (C) 2020 by Savoir-faire Linux
-* Authors: William Enright <william.enright@savoirfairelinux.com>
-*          Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com>
-*          Johnny Flores <johnny.flores@savoirfairelinux.com>
-*          Mohammed Raza <mohammed.raza@savoirfairelinux.com>
-*          Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
-*
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
+ * Copyright (C) 2020 by Savoir-faire Linux
+ * Authors: William Enright <william.enright@savoirfairelinux.com>
+ *          Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com>
+ *          Johnny Flores <johnny.flores@savoirfairelinux.com>
+ *          Mohammed Raza <mohammed.raza@savoirfairelinux.com>
+ *          Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
 package net.jami.jams.server.servlets.api.admin.directory;
 
 import com.jsoniter.JsonIterator;
@@ -29,12 +30,12 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
-import lombok.extern.slf4j.Slf4j;
-import net.jami.datastore.main.DataStore;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.authmodule.AuthModuleKey;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.UserProfile;
 
 import java.io.IOException;
@@ -45,32 +46,24 @@ import static net.jami.jams.server.Server.dataStore;
 
 
 @WebServlet("/api/admin/directory/entry")
-@Slf4j
 public class DirectoryEntryServlet extends HttpServlet {
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         //Create a user profile.
-        try {
-            String realm = "LOCAL";
-            UserProfile userProfile = JsonIterator.deserialize(req.getInputStream().readAllBytes(), UserProfile.class);
-            userAuthenticationModule.getAuthSources().get(new AuthModuleKey(realm, AuthenticationSourceType.LOCAL))
-                    .setUserProfile(userProfile);
-
-            resp.setStatus(200);
-            HashMap<String,String> profileName = new HashMap<>();
-            profileName.put("username", userProfile.getUsername());
-            resp.getOutputStream().write(JsonStream.serialize(profileName).getBytes());
-        }
-        catch (Exception e){
-            log.error("Could not store a user profile with error {}",e.getMessage());
-            resp.sendError(500,e.getMessage());
-        }
-
+        String realm = "LOCAL";
+        UserProfile userProfile = JsonIterator.deserialize(req.getInputStream().readAllBytes(), UserProfile.class);
+        userAuthenticationModule.getAuthSources().get(new AuthModuleKey(realm, AuthenticationSourceType.LOCAL))
+            .setUserProfile(userProfile);
+        resp.setStatus(200);
+        HashMap<String, String> profileName = new HashMap<>();
+        profileName.put("username", userProfile.getUsername());
+        resp.getOutputStream().write(JsonStream.serialize(profileName).getBytes());
     }
 
     @Override
-    protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+    protected void doPut(HttpServletRequest req, HttpServletResponse resp) {
         //Update a user's profile.
         try{
             //Check if he is AD/LDAP - then return a 500, because we can't update those profile datas.
@@ -91,6 +84,7 @@ public class DirectoryEntryServlet extends HttpServlet {
     }
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         //This method will probably never be implemented.
     }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java
index e30132d3..333e55f6 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/SubscriptionServlet.java
@@ -29,7 +29,9 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import net.jami.jams.ca.JamsCA;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.objects.responses.SubscriptionStatusResponse;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.server.Server;
 
 import java.io.IOException;
@@ -39,6 +41,7 @@ public class SubscriptionServlet extends HttpServlet {
 
     //Get the subscription status (see: SubscriptionStatusResponse.class)
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
         resp.setContentType("application/json");
@@ -51,6 +54,7 @@ public class SubscriptionServlet extends HttpServlet {
     //Upload the license here, which is really just uploading a base64 representation of the keypair - and store it
     // on disk..
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         req.getInputStream().readAllBytes();
 
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java
index 1b3a5e45..b1d0aa58 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/update/UpdateServlet.java
@@ -28,6 +28,8 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.updater.FullSystemStatusResponse;
 
 import java.io.IOException;
@@ -39,6 +41,7 @@ public class UpdateServlet extends HttpServlet {
 
     //Return the current version number and the available version number.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         FullSystemStatusResponse response = new FullSystemStatusResponse();
         response.setLocalVersions(appUpdater.getLocalVersions());
@@ -49,6 +52,7 @@ public class UpdateServlet extends HttpServlet {
 
     //This is the do-update button.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
         if(appUpdater.getUpdateAvailable()){
             appUpdater.doUpdate();
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java
index 252d8814..d8ad89d7 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UserServlet.java
@@ -28,10 +28,12 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.dao.StatementElement;
 import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.responses.DeviceRevocationResponse;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.utils.PasswordGenerator;
 import net.jami.jams.server.core.workflows.RevokeUserFlow;
@@ -48,6 +50,7 @@ public class UserServlet extends HttpServlet {
 
     //Get the user profile.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         StatementList statementList = new StatementList();
         StatementElement st1 = new StatementElement("username","=",req.getParameter("username"),"");
@@ -57,6 +60,7 @@ public class UserServlet extends HttpServlet {
 
     //Create an internal user - this is always technically available, because internal users have the right to exist.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         User user = new User();
         user.setUsername(req.getParameter("username"));
@@ -76,6 +80,7 @@ public class UserServlet extends HttpServlet {
 
     //Update user data.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         String username = req.getParameter("username");
         //Check if he is AD/LDAP - then return a 403, because we can't set such password.
@@ -95,6 +100,7 @@ public class UserServlet extends HttpServlet {
 
     //Revoke a user.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         DeviceRevocationResponse devResponse = RevokeUserFlow.revokeUser(req.getParameter("username"));
         if(devResponse != null && devResponse.isSuccess()) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes());
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java
index 179f241d..d3dd0e4b 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/admin/users/UsersServlet.java
@@ -28,6 +28,8 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
+import net.jami.jams.common.objects.user.AccessLevel;
 
 import java.io.IOException;
 
@@ -38,6 +40,7 @@ public class UsersServlet extends HttpServlet {
 
     //Returns a list of users.
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         resp.getOutputStream().write(JsonStream.serialize(dataStore.getUserDao().getObjects(null).get(0)).getBytes());
     }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
index cb7123b5..c80845ad 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/device/DeviceServlet.java
@@ -42,7 +42,6 @@ import net.jami.jams.server.core.workflows.RegisterDeviceFlow;
 import net.jami.jams.server.core.workflows.RevokeDeviceFlow;
 
 import java.io.IOException;
-import java.util.stream.Collectors;
 
 import static net.jami.jams.server.Server.certificateAuthority;
 import static net.jami.jams.server.Server.dataStore;
@@ -171,8 +170,9 @@ public class DeviceServlet extends HttpServlet {
         StatementElement statementElement = new StatementElement("owner","=",req.getAttribute("username").toString(),"");
         statementList.addStatement(statementElement);
         if(dataStore.getDeviceDao().getObjects(statementList).stream().filter(device ->
-            device.getDeviceId().equals(deviceId)).collect(Collectors.toList()).size() == 0){
+            device.getDeviceId().equals(deviceId)).count() == 0){
             TomcatCustomErrorHandler.sendCustomError(resp,403,"You do not have sufficient rights to revoke this device!");
+            return;
         }
         DeviceRevocationResponse devResponse = RevokeDeviceFlow.revokeDevice(req.getAttribute("username").toString(),deviceId);
         if(devResponse != null) resp.getOutputStream().write(JsonStream.serialize(devResponse).getBytes());
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
index 1f940c8b..e236985f 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/directory/SearchDirectoryServlet.java
@@ -23,7 +23,6 @@
 package net.jami.jams.server.servlets.api.auth.directory;
 
 import com.jsoniter.output.JsonStream;
-import jakarta.servlet.ServletException;
 import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java
index 752d944a..80c5aee1 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/user/UserServlet.java
@@ -22,7 +22,6 @@
 */
 package net.jami.jams.server.servlets.api.auth.user;
 
-import com.jsoniter.output.JsonStream;
 import jakarta.servlet.ServletException;
 import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
@@ -35,14 +34,11 @@ import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.User;
 import net.jami.jams.common.utils.PasswordGenerator;
-import org.bouncycastle.openssl.PEMWriter;
 
 import java.io.IOException;
-import java.io.StringWriter;
 
 import static net.jami.jams.server.Server.certificateAuthority;
 import static net.jami.jams.server.Server.dataStore;
-import static net.jami.jams.server.Server.nameServer;
 
 @WebServlet("/api/auth/user")
 public class UserServlet extends HttpServlet {
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java
index d0637660..51a757fb 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateAuthSourceServlet.java
@@ -30,8 +30,10 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.objects.requests.CreateAuthSourceRequest;
+import net.jami.jams.common.objects.user.AccessLevel;
 
 import java.io.IOException;
 
@@ -42,11 +44,13 @@ import static net.jami.jams.server.Server.userAuthenticationModule;
 public class CreateAuthSourceServlet extends HttpServlet {
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         super.doGet(req, resp);
     }
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         CreateAuthSourceRequest authSourceRequest = JsonIterator.deserialize(
                 req.getInputStream().readAllBytes(),CreateAuthSourceRequest.class);
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java
index b8c54864..7a813d1d 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateCAServlet.java
@@ -28,7 +28,9 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.objects.requests.CreateCARequest;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.utils.Validator;
 
 import java.io.IOException;
@@ -37,11 +39,13 @@ import java.io.IOException;
 public class CreateCAServlet extends HttpServlet {
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         super.doGet(req, resp);
     }
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         CreateCARequest caRequest = JsonIterator.deserialize(req.getInputStream().readAllBytes(),CreateCARequest.class);
         if(!Validator.validateCARequests(caRequest)){
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java
index d7ac12d9..71ee86d1 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/CreateServerSettingsServlet.java
@@ -28,7 +28,9 @@ import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
+import net.jami.jams.common.annotations.ScopedServletMethod;
 import net.jami.jams.common.cryptoengineapi.CertificateAuthorityConfig;
+import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.server.core.workflows.InstallationFinalizer;
 
 import java.io.IOException;
@@ -39,11 +41,13 @@ public class CreateServerSettingsServlet extends HttpServlet {
     InstallationFinalizer finalizer = new InstallationFinalizer();
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
         super.doGet(req, resp);
     }
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         CertificateAuthorityConfig config = JsonIterator.deserialize(
                 req.getInputStream().readAllBytes(),CertificateAuthorityConfig.class);
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java
index 1a9ce243..f16a24e7 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/InstallLastStepServlet.java
@@ -28,6 +28,8 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import net.jami.jams.ca.JamsCA;
+import net.jami.jams.common.annotations.ScopedServletMethod;
+import net.jami.jams.common.objects.user.AccessLevel;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -36,6 +38,7 @@ import java.util.HashMap;
 public class InstallLastStepServlet extends HttpServlet {
 
     @Override
+    @ScopedServletMethod(securityGroups = {AccessLevel.ADMIN})
     protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
         resp.setHeader("Access-Control-Allow-Origin", JamsCA.serverDomain);
         HashMap<String,String> payload = new HashMap<>();
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java
index 5475a954..424f47a5 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/install/StartInstallServlet.java
@@ -32,8 +32,6 @@ import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
 import net.jami.jams.common.authentication.AuthenticationSourceType;
 import net.jami.jams.common.authmodule.AuthTokenResponse;
-import net.jami.jams.common.dao.StatementElement;
-import net.jami.jams.common.dao.StatementList;
 import net.jami.jams.common.objects.requests.CredentialsRequest;
 import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.objects.user.User;
@@ -43,6 +41,9 @@ import java.io.IOException;
 import static net.jami.jams.server.Server.dataStore;
 import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth;
 
+/*
+This is not scoped because it is called once.
+ */
 @Slf4j
 @WebServlet("/api/install/start")
 public class StartInstallServlet extends HttpServlet {
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java
deleted file mode 100644
index 29b41cca..00000000
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AdminApiFilter.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-* Copyright (C) 2020 by Savoir-faire Linux
-* Authors: William Enright <william.enright@savoirfairelinux.com>
-*          Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com>
-*          Johnny Flores <johnny.flores@savoirfairelinux.com>
-*          Mohammed Raza <mohammed.raza@savoirfairelinux.com>
-*          Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
-*
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
-package net.jami.jams.server.servlets.filters;
-
-import com.nimbusds.jose.JWSVerifier;
-import com.nimbusds.jose.crypto.RSASSAVerifier;
-import com.nimbusds.jwt.SignedJWT;
-import jakarta.servlet.Filter;
-import jakarta.servlet.FilterChain;
-import jakarta.servlet.ServletException;
-import jakarta.servlet.ServletRequest;
-import jakarta.servlet.ServletResponse;
-import jakarta.servlet.annotation.WebFilter;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
-import lombok.extern.slf4j.Slf4j;
-import net.jami.jams.common.objects.user.AccessLevel;
-import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
-import net.jami.jams.server.Server;
-
-import java.io.IOException;
-import java.util.HashSet;
-
-import static net.jami.jams.server.Server.userAuthenticationModule;
-import static net.jami.jams.server.servlets.filters.JWTValidator.verifyLevel;
-import static net.jami.jams.server.servlets.filters.JWTValidator.verifyValidity;
-
-@WebFilter(urlPatterns = {"/api/admin/*"})
-@Slf4j
-public class AdminApiFilter implements Filter {
-
-    private static final AccessLevel TARGET_LEVEL = AccessLevel.ADMIN;
-
-    @Override
-    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
-        HttpServletRequest request = (HttpServletRequest) servletRequest;
-        HttpServletResponse response = (HttpServletResponse) servletResponse;
-        if (!Server.isInstalled.get()) {
-            TomcatCustomErrorHandler.sendCustomError(response,404,"Server is not installed yet!");
-        } else {
-            boolean authsuccess = false;
-            boolean isLogin = false;
-            if (request.getServletPath().contains("login")) isLogin = true;
-            if(request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")){
-                SignedJWT signedJWT = null;
-                try {
-                    JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey());
-                    signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer",""));
-                    HashSet<AccessLevel> permissionLevels = new HashSet<>();
-                    permissionLevels.add(AccessLevel.ADMIN);
-                    if(signedJWT.verify(jwsVerifier) && verifyValidity(signedJWT) && verifyLevel(signedJWT,permissionLevels)){
-                        authsuccess = true;
-                        request.setAttribute("username",signedJWT.getJWTClaimsSet().getSubject());
-                    }
-                } catch (Exception e) {
-                    log.info("Received an invalid token, declining access...");
-                }
-            }
-            if (authsuccess || isLogin){
-                filterChain.doFilter(servletRequest, servletResponse);
-            }
-            else TomcatCustomErrorHandler.sendCustomError(response,401,"You are not authenticated!");
-        }
-    }
-
-}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java
index 6e40c466..5d2384aa 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/ApiFilter.java
@@ -23,9 +23,6 @@
 
 package net.jami.jams.server.servlets.filters;
 
-import com.nimbusds.jose.JWSVerifier;
-import com.nimbusds.jose.crypto.RSASSAVerifier;
-import com.nimbusds.jwt.SignedJWT;
 import jakarta.servlet.Filter;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
@@ -35,18 +32,18 @@ import jakarta.servlet.annotation.WebFilter;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.extern.slf4j.Slf4j;
-import net.jami.jams.common.authmodule.AuthTokenResponse;
-import net.jami.jams.common.objects.user.AccessLevel;
 import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
 import net.jami.jams.server.Server;
 
 import java.io.IOException;
 
-import static net.jami.jams.server.Server.userAuthenticationModule;
-import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth;
-import static net.jami.jams.server.servlets.filters.JWTValidator.verifyValidity;
+import static net.jami.jams.server.servlets.filters.FilterUtils.doAuthCheck;
 
-@WebFilter(urlPatterns = {"/api/auth/*"})
+/**
+ * Since we have the @ScopedServletMethod annotation, the admin filter became absolutely useless
+ * as we can simply scope things.
+ */
+@WebFilter(urlPatterns = {"/api/auth/*","/api/admin/*"})
 @Slf4j
 public class ApiFilter implements Filter {
 
@@ -59,42 +56,8 @@ public class ApiFilter implements Filter {
         } else {
             boolean authsuccess = false;
             boolean isLogin = false;
-            if (request.getServletPath().contains("login")) {
-                isLogin = true;
-            }
-            //We need to support 2 types of authorization tokens here: the basic and the bearer.
-            if(request.getHeader("authorization") != null){
-                AuthTokenResponse res = null;
-                if(request.getHeader("authorization").contains("basic")) {
-                    try {
-                        res = processUsernamePasswordAuth(request.getHeader("authorization"));
-                        SignedJWT token = SignedJWT.parse(res.getAccess_token());
-                        request.setAttribute("username", token.getJWTClaimsSet().getSubject());
-                        request.setAttribute("accessLevel", AccessLevel.valueOf(token.getJWTClaimsSet().getClaim("scope").toString()));
-                    } catch (Exception e) {
-                        log.error("Could not authenticate user!");
-                    }
-                    if (res != null) authsuccess = true;
-                }
-                else if(request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")){
-                    SignedJWT signedJWT = null;
-                    try {
-                        JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey());
-                        signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer",""));
-                        //In this case, we need to ask the "target" resource what are the allowed access levels.
-                        if (signedJWT.verify(jwsVerifier) && verifyValidity(signedJWT)) {
-                            authsuccess = true;
-                            request.setAttribute("username", signedJWT.getJWTClaimsSet().getSubject());
-                            if ((Boolean) signedJWT.getJWTClaimsSet().getClaim("oneTimePassword")) {
-                                //TODO: use redirect to enforce the /changepassword url or something.
-                            }
-                            request.setAttribute("accessLevel", AccessLevel.valueOf(signedJWT.getJWTClaimsSet().getClaim("scope").toString()));
-                        }
-                    } catch (Exception e) {
-                        log.info("Received an invalid token, declining access...");
-                    }
-                }
-            }
+            if (request.getServletPath().contains("login")) isLogin = true;
+            else authsuccess = doAuthCheck(request);
             if (authsuccess || isLogin) {
                 filterChain.doFilter(servletRequest, servletResponse);
             } else {
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java
new file mode 100644
index 00000000..22457b4d
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/AuthRequestType.java
@@ -0,0 +1,8 @@
+package net.jami.jams.server.servlets.filters;
+
+public enum AuthRequestType {
+    FORM, //not supported on filters.
+    BASIC,
+    BEARER_TOKEN,
+    CLIENT_CERT //not supported on filters.
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java
new file mode 100644
index 00000000..6ae93786
--- /dev/null
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/FilterUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2020 by Savoir-faire Linux
+ * Authors: William Enright <william.enright@savoirfairelinux.com>
+ *          Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com>
+ *          Johnny Flores <johnny.flores@savoirfairelinux.com>
+ *          Mohammed Raza <mohammed.raza@savoirfairelinux.com>
+ *          Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+package net.jami.jams.server.servlets.filters;
+
+import com.nimbusds.jose.JWSVerifier;
+import com.nimbusds.jose.crypto.RSASSAVerifier;
+import com.nimbusds.jwt.SignedJWT;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.extern.slf4j.Slf4j;
+import net.jami.jams.common.objects.user.AccessLevel;
+
+import java.util.Date;
+
+import static net.jami.jams.server.Server.userAuthenticationModule;
+import static net.jami.jams.server.servlets.api.auth.login.AuthRequestProcessor.processUsernamePasswordAuth;
+
+@Slf4j
+public class FilterUtils {
+
+    public static final String USERNAME_ATTR = "username";
+    public static final String ACCESS_LEVEL_ATTR = "accessLevel";
+
+    public static boolean verifyValidity(SignedJWT signedJWT) {
+        try {
+            return signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    public static AuthRequestType classifyRequest(HttpServletRequest request) {
+        if (request.getHeader("authorization") != null) {
+            if (request.getHeader("authorization").contains("basic") || request.getHeader("authorization").contains("Basic")) {
+                return AuthRequestType.BASIC;
+            }
+            if (request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer")) {
+                return AuthRequestType.BEARER_TOKEN;
+            }
+            return null;
+        }
+        return null;
+    }
+
+    public static boolean doAuthCheck(HttpServletRequest request){
+        AuthRequestType requestType = FilterUtils.classifyRequest(request);
+        try {
+            if (requestType != null) {
+                SignedJWT token = null;
+                switch (requestType) {
+                    case BASIC:
+                        token = SignedJWT.parse(processUsernamePasswordAuth(request.getHeader("authorization")).getAccess_token());
+                        break;
+                    case BEARER_TOKEN:
+                        token = SignedJWT.parse(request.getHeader("authorization").
+                            replace("bearer", "")
+                            .replace("Bearer", ""));
+                        break;
+                    default:
+                        return false;
+                }
+                JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey());
+                if (token.verify(jwsVerifier) && verifyValidity(token)) {
+                    request.setAttribute(USERNAME_ATTR, token.getJWTClaimsSet().getSubject());
+                    request.setAttribute(ACCESS_LEVEL_ATTR, AccessLevel.valueOf(token.getJWTClaimsSet().getClaim("scope").toString()));
+                    return true;
+                }
+            }
+            return false;
+        }
+        catch (Exception e){
+            log.info("Failed to process authentication request and denying access: {}",e.getMessage());
+            return false;
+        }
+    }
+}
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java
index b6213ab2..478fe16c 100644
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java
+++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/InstallFilter.java
@@ -22,9 +22,6 @@
 */
 package net.jami.jams.server.servlets.filters;
 
-import com.nimbusds.jose.JWSVerifier;
-import com.nimbusds.jose.crypto.RSASSAVerifier;
-import com.nimbusds.jwt.SignedJWT;
 import jakarta.servlet.Filter;
 import jakarta.servlet.FilterChain;
 import jakarta.servlet.ServletException;
@@ -38,9 +35,8 @@ import net.jami.jams.common.serialization.tomcat.TomcatCustomErrorHandler;
 import net.jami.jams.server.Server;
 
 import java.io.IOException;
-import java.util.Date;
 
-import static net.jami.jams.server.Server.userAuthenticationModule;
+import static net.jami.jams.server.servlets.filters.FilterUtils.doAuthCheck;
 
 @WebFilter("/api/install/*")
 @Slf4j
@@ -57,20 +53,7 @@ public class InstallFilter implements Filter {
             boolean authsuccess = false;
             boolean isLogin = false;
             if(request.getServletPath().contains("start")) isLogin = true;
-            SignedJWT signedJWT = null;
-            if(request.getHeader("authorization") != null && (request.getHeader("authorization").contains("bearer") || request.getHeader("authorization").contains("Bearer"))){
-                try {
-                    JWSVerifier jwsVerifier = new RSASSAVerifier(userAuthenticationModule.getAuthModulePubKey());
-                    signedJWT = SignedJWT.parse(request.getHeader("authorization").replace("bearer","").replace("Bearer",""));
-                    if (signedJWT.verify(jwsVerifier) && signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0) {
-                        authsuccess = true;
-                        request.setAttribute("username", signedJWT.getJWTClaimsSet().getSubject());
-                        request.setAttribute("accessLevel", signedJWT.getJWTClaimsSet().getClaim("scope"));
-                    }
-                } catch (Exception e) {
-                    log.info("Received an invalid token, declining access...");
-                }
-            }
+            else authsuccess = doAuthCheck(request);
             if(authsuccess || isLogin) filterChain.doFilter(servletRequest,servletResponse);
             else TomcatCustomErrorHandler.sendCustomError(response,401,"You are not authenticated!");
         }
diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java
deleted file mode 100644
index 874f7666..00000000
--- a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/JWTValidator.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
-* Copyright (C) 2020 by Savoir-faire Linux
-* Authors: William Enright <william.enright@savoirfairelinux.com>
-*          Ndeye Anna Ndiaye <anna.ndiaye@savoirfairelinux.com>
-*          Johnny Flores <johnny.flores@savoirfairelinux.com>
-*          Mohammed Raza <mohammed.raza@savoirfairelinux.com>
-*          Felix Sidokhine <felix.sidokhine@savoirfairelinux.com>
-*
-*
-* This program is free software; you can redistribute it and/or modify
-* it under the terms of the GNU General Public License as published by
-* the Free Software Foundation; either version 3 of the License, or
-* (at your option) any later version.
-*
-* This program is distributed in the hope that it will be useful,
-* but WITHOUT ANY WARRANTY; without even the implied warranty of
-* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-* GNU General Public License for more details.
-*
-* You should have received a copy of the GNU General Public License
-* along with this program.  If not, see <https://www.gnu.org/licenses/>.
-*/
-package net.jami.jams.server.servlets.filters;
-
-import com.nimbusds.jwt.SignedJWT;
-import net.jami.jams.common.objects.user.AccessLevel;
-
-import java.util.Date;
-import java.util.Set;
-
-public class JWTValidator {
-
-    public static boolean verifyLevel(SignedJWT signedJWT, Set<AccessLevel> targetLevels){
-        try {
-            return targetLevels.contains(AccessLevel.valueOf(signedJWT.getJWTClaimsSet().getClaim("scope").toString()));
-        }
-        catch (Exception e){
-            return false;
-        }
-    }
-
-    public static boolean verifyValidity(SignedJWT signedJWT){
-        try {
-            return signedJWT.getJWTClaimsSet().getExpirationTime().compareTo(new Date()) > 0;
-        }
-        catch (Exception e){
-            return false;
-        }
-    }
-}
diff --git a/jams-server/src/main/resources/webapp/js/api.js b/jams-server/src/main/resources/webapp/js/api.js
index 67ee6704..cb551e30 100644
--- a/jams-server/src/main/resources/webapp/js/api.js
+++ b/jams-server/src/main/resources/webapp/js/api.js
@@ -138,7 +138,7 @@ function setJWT(value) {
 
 function set_installation_response(url) {
     return function (data, statusCode, jqXHR) {
-        // endpoint does not exists
+        // endpoint does not exist - revoke
         if (data) {
             if (data.status == 404) {
                 $('#installCompleteModalCenter').modal('show');
diff --git a/jams-server/src/main/resources/webapp/js/user.js b/jams-server/src/main/resources/webapp/js/user.js
index 676ce2c5..917c3e26 100644
--- a/jams-server/src/main/resources/webapp/js/user.js
+++ b/jams-server/src/main/resources/webapp/js/user.js
@@ -149,7 +149,10 @@ $(document).ready(function() {
             deviceId = $('#revokeDeviceModal').attr("data-device-id");
             isSearch = false;
             $('.loading').show();
-            ajaxApiCall(api_path_delete_auth_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler);
+            if (getAdminStatus())
+                ajaxApiCall(api_path_delete_admin_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler);
+            else
+                ajaxApiCall(api_path_delete_auth_device_revoke + "?deviceId=" + deviceId, 'DELETE', null, null, revokeDeviceHandler);
         });
 
         $('.dismiss-device').on('click', function(){
@@ -346,7 +349,6 @@ function setUserDevices(data) {
 }
 
 function revokeUser(data, statusCode, jqXHR) {
-
     if (jqXHR.status == 200) {
         $('#status-whatever').text("Revoked");
         $('#status-whatever').addClass("text-danger");
diff --git a/ldap-connector/pom.xml b/ldap-connector/pom.xml
index 9c87c3bc..f0a74061 100644
--- a/ldap-connector/pom.xml
+++ b/ldap-connector/pom.xml
@@ -23,8 +23,25 @@
             <artifactId>ldaptive</artifactId>
             <version>${ldaptive.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.zapodot</groupId>
+            <artifactId>embedded-ldap-junit</artifactId>
+            <version>0.8.1</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
+    <repositories>
+        <repository>
+            <id>forgerock</id>
+            <url>https://maven.forgerock.org/repo/community//</url>
+        </repository>
+        <repository>
+            <id>dcache</id>
+            <url>http://download.dcache.org/nexus/content/repositories/releases/</url>
+        </repository>
+    </repositories>
+
     <build>
         <plugins>
             <plugin>
diff --git a/ldap-connector/src/test/java/tests/GenericLDAPTest.java b/ldap-connector/src/test/java/tests/GenericLDAPTest.java
new file mode 100644
index 00000000..be561469
--- /dev/null
+++ b/ldap-connector/src/test/java/tests/GenericLDAPTest.java
@@ -0,0 +1,61 @@
+package tests;
+
+import net.jami.jams.common.objects.user.UserProfile;
+import net.jami.jams.ldap.connector.LDAPConnector;
+import org.junit.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.zapodot.junit.ldap.EmbeddedLdapRule;
+import org.zapodot.junit.ldap.EmbeddedLdapRuleBuilder;
+
+import java.io.InputStream;
+
+public class GenericLDAPTest {
+
+    private static LDAPConnector ldapConnector = null;
+
+    private void initLdapConnector() throws Exception{
+        if(ldapConnector == null) {
+            InputStream inputStream = GenericLDAPTest.class.getClassLoader().getResourceAsStream("ldapconfig.json");
+            ldapConnector = new LDAPConnector(new String(inputStream.readAllBytes()));
+        }
+    }
+
+    @Rule
+    public EmbeddedLdapRule server = EmbeddedLdapRuleBuilder
+        .newInstance()
+        .usingDomainDsn("dc=savoirfairelinux,dc=net")
+        .bindingToPort(1089)
+        .importingLdifs("bootstrap.ldif")
+        .build();
+
+    @Test
+    public void testLookUp() throws Exception{
+        initLdapConnector();
+        UserProfile[] profiles = ldapConnector.getUserProfile("*","FULL_TEXT_NAME");
+        Assertions.assertEquals(2,profiles.length);
+        Assertions.assertNotNull(profiles[0].getFirstName());
+        Assertions.assertNotNull(profiles[1].getFirstName());
+    }
+
+    @Test
+    public void testAuth() throws Exception{
+        boolean res;
+        initLdapConnector();
+        res = ldapConnector.authenticate("fsidokhine","password");
+        Assertions.assertTrue(res);
+        res = ldapConnector.authenticate("fsidokhine","badpassword");
+        Assertions.assertFalse(res);
+    }
+
+    @Test
+    public void getVcard() throws Exception{
+        initLdapConnector();
+        UserProfile[] profiles = ldapConnector.getUserProfile("Felix","FULL_TEXT_NAME");
+        Assert.assertEquals(1,profiles.length);
+        String vcard = profiles[0].getAsVCard();
+        Assert.assertNotNull(vcard);
+    }
+
+}
diff --git a/ldap-connector/src/test/resources/bootstrap.ldif b/ldap-connector/src/test/resources/bootstrap.ldif
new file mode 100644
index 00000000..cc1b2908
--- /dev/null
+++ b/ldap-connector/src/test/resources/bootstrap.ldif
@@ -0,0 +1,45 @@
+dn: dc=savoirfairelinux,dc=net
+objectClass: top
+objectClass: domain
+
+dn: ou=dsa,dc=savoirfairelinux,dc=net
+ou: group
+objectClass: top
+objectclass: organizationalunit
+
+dn: cn=sipallow,ou=dsa,dc=savoirfairelinux,dc=net
+objectClass: posixAccount
+objectClass: inetorgperson
+objectClass: organizationalperson
+objectClass: top
+uid: sipallow
+userPassword: password
+
+dn: ou=users,dc=savoirfairelinux,dc=net
+ou: people
+objectClass: top
+objectclass: organizationalunit
+
+dn: uid=fsidokhine,ou=users,dc=savoirfairelinux,dc=net
+objectClass: posixAccount
+objectClass: inetorgperson
+objectClass: organizationalperson
+objectClass: top
+uid: fsidokhine
+userPassword: password
+givenName: Felix
+cn: Felix Sidokhine
+sn: Sidokhine
+mail: felix.sidokhine@savoirfairelinux.com
+
+dn: uid=aberaud,ou=users,dc=savoirfairelinux,dc=net
+objectClass: posixAccount
+objectClass: inetorgperson
+objectClass: organizationalperson
+objectClass: top
+uid: aberaud
+userPassword: password
+givenName: Adrien
+cn: Adrien Beraud
+sn: Beraud
+mail: adrien.beraud@savoirfairelinux.com
\ No newline at end of file
diff --git a/ldap-connector/src/test/resources/ldapconfig.json b/ldap-connector/src/test/resources/ldapconfig.json
new file mode 100644
index 00000000..39a9f61d
--- /dev/null
+++ b/ldap-connector/src/test/resources/ldapconfig.json
@@ -0,0 +1,20 @@
+{
+  "useStartTLS": false,
+  "realm": "savoirfairelinux",
+  "baseDN": "ou=users,dc=savoirfairelinux,dc=net",
+  "host": "ldap://localhost:1089",
+  "username": "cn=sipallow,ou=dsa,dc=savoirfairelinux,dc=net",
+  "password": "password",
+  "usernameField": "uid",
+  "fieldMappings": {
+    "givenName": "FirstName",
+    "sn": "LastName",
+    "jpegPhoto": "ProfilePicture",
+    "mail": "Email",
+    "telephoneNumber": "PhoneNumber",
+    "mobile": "MobileNumber",
+    "facsimileTelephoneNumber": "FaxNumber",
+    "extensionName": "PhoneNumberExtension",
+    "o": "Organization"
+  }
+}
\ No newline at end of file
-- 
GitLab