diff --git a/classes/IdTokenBuilder.php b/classes/IdTokenBuilder.php
index 18d3a16a08d1c088ad2bed13f5bd88dd95a7711d..456a678910fa15f8e8fc32f01faa5b7179f69745 100644
--- a/classes/IdTokenBuilder.php
+++ b/classes/IdTokenBuilder.php
@@ -22,12 +22,12 @@ class IdTokenBuilder {
     }
 
     private function createPayloadArray(AccessToken $accessToken, string $nonce = null) {
-
+        
         $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
 
         $payloadArr = array(
             'iss' => $this->locator->config->jwtIssuer,
-            'sub' => $user->id,
+            'sub' => strval($user->id),
             'iat' => intval($accessToken->creationTime),
             'exp' => intval($accessToken->expirationTime),
             'name' => $user->getCompleteName(),
@@ -49,6 +49,10 @@ class IdTokenBuilder {
             }
         }
 
+        if ($accessToken->joinUser !== null) {
+            $payloadArr['alt_sub'] = strval($accessToken->joinUser);
+        }
+
         return $payloadArr;
     }
 
diff --git a/classes/UserHandler.php b/classes/UserHandler.php
index cc29a1a7266b46476a2f7098a003d60901e7a19c..318f5230ade99769f1a87ecb28d9a8466e739484 100644
--- a/classes/UserHandler.php
+++ b/classes/UserHandler.php
@@ -63,44 +63,28 @@ class UserHandler {
         }
     }
 
-    /**
-     * Build an URL for the web service endpoint that needs to be called in order
-     * to move groups from one user to the other during a join operation.
-     * @return string grouper URL for the PrepareToJoinServlet
-     */
-    private function getJoinURL() {
-        $joinURL = $this->grouperConfig['wsURL'];
-
-        if (substr($joinURL, -1) !== '/') {
-            $joinURL .= '/';
-        }
-        $joinURL .= 'ia2join';
-
-        return $joinURL;
-    }
-
     public function joinUsers(User $user1, User $user2): User {
 
         $userId1 = $user1->id;
         $userId2 = $user2->id;
 
         // Call Grouper for moving groups and privileges from one user to the other
-        if (isset($this->locator->config->gmsConfig)) {
+        if (isset($this->locator->config->gms)) {
 
             // TODO: change with new GMS
             //create cURL connection
-            $conn = curl_init($this->getJoinURL());
+            $conn = curl_init($this->locator->config->gms->joinEndpoint);
 
             //set options
             curl_setopt($conn, CURLOPT_CONNECTTIMEOUT, 30);
             curl_setopt($conn, CURLOPT_RETURNTRANSFER, true);
             curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true);
             curl_setopt($conn, CURLOPT_FOLLOWLOCATION, 1);
-            curl_setopt($conn, CURLOPT_USERPWD, $this->grouperConfig['user'] . ":" . $this->grouperConfig['password']);
+            curl_setopt($conn, CURLOPT_HTTPHEADER, ['Authorization: Bearer '
+                . $this->getJoinIdToken($userId1, $userId2)]);
 
             //set data to be posted
             curl_setopt($conn, CURLOPT_POST, 1);
-            curl_setopt($conn, CURLOPT_POSTFIELDS, "subject1Id=RAP:$userId1&subject2Id=RAP:$userId2");
 
             //perform the request
             $response = curl_exec($conn);
@@ -111,8 +95,9 @@ class UserHandler {
             } else {
                 //show information regarding the error
                 curl_close($conn);
+                error_log($response);
                 http_response_code(500);
-                die('Error: Grouper response code: ' . $info['http_code']);
+                die('Error: GMS response code: ' . $info['http_code'] . "\n");
             }
         }
 
@@ -128,4 +113,19 @@ class UserHandler {
         return $user1;
     }
 
+    private function getJoinIdToken(int $userId1, int $userId2): string {
+
+        $gmsId = $this->locator->config->gms->id;
+
+        $accessToken = new AccessToken();
+        $accessToken->clientId = $gmsId;
+        $accessToken->userId = $userId1;
+        $accessToken->joinUser = $userId2;
+        // shorter expiration
+        $accessToken->expirationTime = $accessToken->creationTime + 100;
+        $accessToken->scope = ['openid'];
+
+        return $this->locator->getIdTokenBuilder()->getIdToken($accessToken);
+    }
+
 }
diff --git a/classes/model/AccessToken.php b/classes/model/AccessToken.php
index 9e6da47103792e9c8028dd853daa5caee0011b3a..43527ac2960aa25c1d854f8d9350f48cd8ff7f3d 100644
--- a/classes/model/AccessToken.php
+++ b/classes/model/AccessToken.php
@@ -19,6 +19,7 @@ class AccessToken {
     public $redirectUri;
     public $clientId;
     public $scope;
+    public $joinUser;
 
     public function isExpired(): bool {
         return $this->expirationTime < time();
diff --git a/config-example.json b/config-example.json
index 9cf0321eaae85e8abb44f37ba3557520b30e283a..db809602b8f8d1ca213b1fa3c1fc7dbc4c2e7249 100644
--- a/config-example.json
+++ b/config-example.json
@@ -45,8 +45,7 @@
         }
     },
     "gms": {
-        "baseUrl": "https://sso.ia2.inaf.it/gms",
-        "clientId": "rap",
-        "clientSecret": "rap-secret"
+        "id": "gms",
+        "joinEndpoint": "http://localhost:8082/gms/ws/jwt/join"
     }
 }
diff --git a/exec/join.php b/exec/join.php
new file mode 100644
index 0000000000000000000000000000000000000000..8a3689f2d5ec5d46e6238d7ef35c7514cdebff28
--- /dev/null
+++ b/exec/join.php
@@ -0,0 +1,31 @@
+<?php
+
+if($argc !== 3) {
+    echo "Usage: php $argv[0] <user_id_1> <user_id_2>\n";
+    echo "The second id will be deleted.\n";
+    exit(1);
+}
+
+chdir(dirname(__FILE__));
+
+include '../include/init.php';
+
+$dao = $locator->getUserDAO();
+$handler = $locator->getUserHandler();
+$tokenBuilder = $locator->getIdTokenBuilder();
+
+$user1 = $dao->findUserById((int) $argv[1]);
+if($user1 === null) {
+    echo "User $argv[1] not found";
+    exit(1);
+}
+
+$user2 = $dao->findUserById((int) $argv[2]);
+if($user2 === null) {
+    echo "User $argv[2] not found";
+    exit(1);
+}
+
+$handler->joinUsers($user1, $user2);
+
+echo "OK\n";
diff --git a/include/rest-web-service.php b/include/rest-web-service.php
index 89e738b9b59f8b4449417db619551df95b68bae0..d67366121d637b1656548286b80797f1b2401055 100644
--- a/include/rest-web-service.php
+++ b/include/rest-web-service.php
@@ -2,41 +2,10 @@
 
 /**
  * REST Web Service using http://flightphp.com/
- * This Web Service should be called only by authorized applications external to
- * RAP, so this directory should be password protected.
- * 
- * Apache configuration:
- * <Location "/rap-ia2/ws">
- *     AuthType basic
- *     AuthName RAP
- *     AuthUserFile rap-service-passwd
- *     Require valid-user
- * </Location>
  */
 //
 $WS_PREFIX = '/ws';
 
-/**
- * Retrieve user information from login token.
- */
-Flight::route('GET ' . $WS_PREFIX . '/user-info', function() {
-
-    global $dao;
-
-    $token = Flight::request()->query['token'];
-    $userData = $dao->findLoginToken($token);
-
-    if (is_null($userData)) {
-        http_response_code(404);
-        die("Token not found");
-    }
-
-    $dao->deleteLoginToken($token);
-
-    header('Content-Type: text/plain');
-    echo $userData;
-});
-
 /**
  * Retrieve user information from user ID.
  */
@@ -117,33 +86,3 @@ Flight::route('POST ' . $WS_PREFIX . '/user', function() {
 
     Flight::json($user);
 });
-
-/**
- * Performs a join.
- */
-Flight::route('POST ' . $WS_PREFIX . '/join', function() {
-
-    global $locator;
-
-    $locator->getOAuth2RequestHandler()->validateToken();
-
-    $postData = Flight::request()->data;
-
-    $userId1 = $postData['user1'];
-    $userId2 = $postData['user2'];
-
-    $user1 = $locator->getUserDAO()->findUserById($userId1);
-    if ($user1 === null) {
-        throw new BadRequestException("User " . $userId1 . " doesn't exists");
-    }
-
-    $user2 = $locator->getUserDAO()->findUserById($userId2);
-    if ($user2 === null) {
-        throw new BadRequestException("User " . $userId2 . " doesn't exists");
-    }
-
-    $locator->getUserHandler()->joinUsers($user1, $user2);
-
-    // if the join has success, returns the remaining user id
-    echo $userId1;
-});