diff --git a/classes/ClientAuthChecker.php b/classes/ClientAuthChecker.php
new file mode 100644
index 0000000000000000000000000000000000000000..cacb8e3ceee6adb1e253cdcaaa899a76958c240f
--- /dev/null
+++ b/classes/ClientAuthChecker.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace RAP;
+
+/**
+ * RFC 6749 specify that in some situations the client must send an Authorization
+ * Basic header containing its credentials (access token in the authorization code
+ * flow and refresh token requests).
+ */
+class ClientAuthChecker {
+
+    private $locator;
+
+    public function __construct(Locator $locator) {
+        $this->locator = $locator;
+    }
+
+    public function validateClientAuth(): void {
+
+        $headers = apache_request_headers();
+
+        if (!isset($headers['Authorization'])) {
+            throw new UnauthorizedException("Missing Authorization header");
+        }
+
+        $authorizationHeader = explode(" ", $headers['Authorization']);
+        if ($authorizationHeader[0] === "Basic") {
+            $basic = explode(':', base64_decode($authorizationHeader[1]));
+            if (count($basic) !== 2) {
+                throw new BadRequestException("Malformed Basic-Auth header");
+            }
+            $clientId = $basic[0];
+            $clientSecret = $basic[1];
+
+            $client = $this->locator->getOAuth2ClientDAO()->getOAuth2ClientByClientId($clientId);
+            if ($client === null) {
+                throw new UnauthorizedException("Client '$clientId' not configured");
+            }
+            if ($clientSecret !== $client->secret) {
+                throw new UnauthorizedException("Invalid client secret");
+            }
+        } else {
+            throw new UnauthorizedException("Expected Basic authorization header");
+        }
+    }
+
+}
diff --git a/classes/Locator.php b/classes/Locator.php
index 6cc736c8a6869cfc0ebf91da009113642c3f7f8f..d032f70b14cbb85d0c17ab8eeed2441df8935fad 100644
--- a/classes/Locator.php
+++ b/classes/Locator.php
@@ -102,6 +102,14 @@ class Locator {
         return new TokenBuilder($this);
     }
 
+    public function getTokenChecker(): TokenChecker {
+        return new TokenChecker($this);
+    }
+
+    public function getClientAuthChecker(): ClientAuthChecker {
+        return new ClientAuthChecker($this);
+    }
+
     /**
      * Retrieve the SessionData object from the $_SESSION PHP variable. Create a
      * new one if it is necessary.
diff --git a/classes/OAuth2RequestHandler.php b/classes/OAuth2RequestHandler.php
index 1ce8a7cf5c6cd61f12a76028aebc5d4d1a726887..12da4751b44284126ad32fe8364d3072984e844d 100644
--- a/classes/OAuth2RequestHandler.php
+++ b/classes/OAuth2RequestHandler.php
@@ -2,8 +2,6 @@
 
 namespace RAP;
 
-use \Firebase\JWT\JWT;
-
 class OAuth2RequestHandler {
 
     private $locator;
@@ -84,7 +82,9 @@ class OAuth2RequestHandler {
                     . '?code=' . $code . '&scope=profile&state=' . $state;
         } else {
             // Implicit grant flow
-            $idToken = $this->locator->getTokenBuilder()->getIdToken($tokenData, $nonce);
+            $idToken = $this->locator->getTokenBuilder()->getIdToken($tokenData, function(& $jwt) use($nonce) {
+                $jwt['nonce'] = $nonce;
+            });
             $redirectUrl = $session->getOAuth2RequestData()->redirectUrl . "#id_token=" . $idToken;
         }
 
@@ -93,6 +93,8 @@ class OAuth2RequestHandler {
 
     public function handleAccessTokenRequest($params): array {
 
+        $this->locator->getClientAuthChecker()->validateClientAuth();
+
         if ($params['code'] === null) {
             throw new BadRequestException("code id is required");
         }
@@ -123,6 +125,8 @@ class OAuth2RequestHandler {
 
     public function handleRefreshTokenRequest($params): array {
 
+        $this->locator->getClientAuthChecker()->validateClientAuth();
+
         if ($params['refresh_token'] === null) {
             throw new BadRequestException("refresh_token is required");
         }
@@ -138,7 +142,6 @@ class OAuth2RequestHandler {
 
         // Generating a new access token
         $accessTokenData = new AccessTokenData();
-        $accessTokenData->token = base64_encode(bin2hex(openssl_random_pseudo_bytes(128)));
         $accessTokenData->clientId = $refreshToken->clientId;
         $accessTokenData->userId = $refreshToken->userId;
         $accessTokenData->scope = $scope;
@@ -186,10 +189,7 @@ class OAuth2RequestHandler {
         $result['token_type'] = 'Bearer';
         $result['expires_in'] = $tokenData->expirationTime - time();
 
-        $refreshToken = base64_encode(bin2hex(openssl_random_pseudo_bytes(128)));
-        $refreshTokenHash = hash('sha256', $refreshToken);
-        $this->storeRefreshTokenData($tokenData, $refreshTokenHash);
-        $result['refresh_token'] = $refreshToken;
+        $result['refresh_token'] = $this->buildRefreshToken($tokenData);
 
         if ($tokenData->scope !== null && in_array('openid', $tokenData->scope)) {
             $result['id_token'] = $this->locator->getTokenBuilder()->getIdToken($tokenData);
@@ -198,93 +198,82 @@ class OAuth2RequestHandler {
         return $result;
     }
 
-    private function storeRefreshTokenData(AccessTokenData $accessTokenData, string $refreshTokenHash): void {
-
-        $refreshToken = new RefreshTokenData();
-        $refreshToken->tokenHash = $refreshTokenHash;
-        $refreshToken->clientId = $accessTokenData->clientId;
-        $refreshToken->userId = $accessTokenData->userId;
-        $refreshToken->scope = $accessTokenData->scope;
-
-        $this->locator->getRefreshTokenDAO()->createRefreshTokenData($refreshToken);
-    }
-
     /**
      * Token introspection endpoint shouldn't be necessary when using OIDC (since
      * tokens are self-contained JWT). This function is kept here for compatibility
      * with some libraries (e.g. Spring Security) but it could be removed in the
      * future.
      */
-    public function handleCheckTokenRequest($token): array {
+    public function handleCheckTokenRequest(): array {
 
-        // TODO: validate the token and expose data
-        $accessToken = $this->locator->getAccessTokenDAO()->getAccessToken($token);
-        if ($accessToken === null) {
-            throw new UnauthorizedException("Invalid access token");
-        }
-
-        $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
+        $jwt = $this->locator->getTokenChecker()->validateToken();
+        $tokenData = $this->getTokenDataFromJwtObject($jwt);
 
         $result = [];
-        $result['exp'] = $accessToken->expirationTime - time();
-        $result['user_name'] = $user->id;
-        $result['client_id'] = $accessToken->clientId;
-        $result['refresh_token'] = $this->storeRefreshTokenData($accessToken);
-
-        if ($accessToken->scope !== null) {
-            $result['scope'] = $accessToken->scope;
-            if (in_array('openid', $accessToken->scope)) {
-                $result['id_token'] = $this->locator->getTokenBuilder()->getIdToken($accessToken);
+        $result['exp'] = $tokenData->expirationTime - time();
+        $result['user_name'] = $tokenData->userId;
+        $result['client_id'] = $tokenData->clientId;
+        $result['access_token'] = $this->copyReceivedAccessToken();
+        $result['refresh_token'] = $this->buildRefreshToken($tokenData);
+
+        if (isset($tokenData->scope) && count($tokenData->scope) > 0) {
+            $result['scope'] = $tokenData->scope;
+            if (in_array('openid', $tokenData->scope)) {
+                $result['id_token'] = $this->locator->getTokenBuilder()->getIdToken($tokenData);
             }
         }
 
         return $result;
     }
 
-    public function validateToken(): void {
+    private function copyReceivedAccessToken(): string {
         $headers = apache_request_headers();
+        return explode(" ", $headers['Authorization'])[1];
+    }
 
-        if (!isset($headers['Authorization'])) {
-            throw new BadRequestException("Missing Authorization header");
-        }
+    private function getTokenDataFromJwtObject($jwt): AccessTokenData {
 
-        $authorizationHeader = explode(" ", $headers['Authorization']);
-        if ($authorizationHeader[0] === "Bearer") {
-            $bearer_token = $authorizationHeader[1];
-        } else {
-            throw new BadRequestException("Invalid token type");
-        }
-
-        $accessToken = $this->locator->getAccessTokenDAO()->getAccessToken($bearer_token);
-        if ($accessToken === null) {
-            $this->attemptJWTTokenValidation($bearer_token);
-        } else if ($accessToken->isExpired()) {
-            throw new UnauthorizedException("Access token is expired");
-        }
+        $tokenData = new AccessTokenData();
+        $tokenData->clientId = $this->getClientIdFromAudience($jwt);
+        $tokenData->userId = $jwt->sub;
+        $tokenData->creationTime = $jwt->iat;
+        $tokenData->expirationTime = $jwt->exp;
+        $tokenData->scope = explode(' ', $jwt->scope);
+        return $tokenData;
     }
 
-    private function attemptJWTTokenValidation($jwt): void {
+    private function getClientIdFromAudience(object $jwt): string {
 
-        $jwtParts = explode('.', $jwt);
-        if (count($jwtParts) === 0) {
-            throw new UnauthorizedException("Invalid token");
+        if (!(isset($jwt->aud))) {
+            throw new UnauthorizedException("Missing 'aud' claim in token");
         }
 
-        $header = JWT::jsonDecode(JWT::urlsafeB64Decode($jwtParts[0]));
-        if (!isset($header->kid)) {
-            throw new UnauthorizedException("Invalid token: missing kid in header");
+        $audience = $jwt->aud;
+        if (is_array($audience)) {
+            if (count($audience) === 0) {
+                throw new UnauthorizedException("Token has empty audience");
+            }
+            return $audience[0];
         }
+        return $audience;
+    }
 
-        $keyPair = $this->locator->getJWKSDAO()->getRSAKeyPairById($header->kid);
-        if ($keyPair === null) {
-            throw new UnauthorizedException("Invalid kid: no key found");
-        }
+    private function buildRefreshToken(AccessTokenData $tokenData): string {
+        $refreshToken = base64_encode(bin2hex(openssl_random_pseudo_bytes(128)));
+        $refreshTokenHash = hash('sha256', $refreshToken);
+        $this->storeRefreshTokenData($tokenData, $refreshTokenHash);
+        return $refreshToken;
+    }
 
-        try {
-            JWT::decode($jwt, $keyPair->publicKey, [$keyPair->alg]);
-        } catch (\Firebase\JWT\ExpiredException $ex) {
-            throw new UnauthorizedException("Access token is expired");
-        }
+    private function storeRefreshTokenData(AccessTokenData $accessTokenData, string $refreshTokenHash): void {
+
+        $refreshToken = new RefreshTokenData();
+        $refreshToken->tokenHash = $refreshTokenHash;
+        $refreshToken->clientId = $accessTokenData->clientId;
+        $refreshToken->userId = $accessTokenData->userId;
+        $refreshToken->scope = $accessTokenData->scope;
+
+        $this->locator->getRefreshTokenDAO()->createRefreshTokenData($refreshToken);
     }
 
 }
diff --git a/classes/TokenBuilder.php b/classes/TokenBuilder.php
index b6a53fab3a5232988bb4b99cb009804229edcf54..abdb369f1610fe423232f3d0a217689bf334ab98 100644
--- a/classes/TokenBuilder.php
+++ b/classes/TokenBuilder.php
@@ -12,16 +12,16 @@ class TokenBuilder {
         $this->locator = $locator;
     }
 
-    public function getIdToken(AccessTokenData $tokenData, string $nonce = null): string {
+    public function getIdToken(AccessTokenData $tokenData, \Closure $jwtCustomizer = null): string {
 
         $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();
 
-        $payload = $this->createIdTokenPayloadArray($tokenData, $nonce);
+        $payload = $this->createIdTokenPayloadArray($tokenData, $jwtCustomizer);
 
         return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
     }
 
-    private function createIdTokenPayloadArray(AccessTokenData $tokenData, string $nonce = null) {
+    private function createIdTokenPayloadArray(AccessTokenData $tokenData, \Closure $jwtCustomizer = null) {
 
         $user = $this->locator->getUserDAO()->findUserById($tokenData->userId);
 
@@ -34,10 +34,6 @@ class TokenBuilder {
             'aud' => $tokenData->clientId
         );
 
-        if ($nonce !== null) {
-            $payloadArr['nonce'] = $nonce;
-        }
-
         if (in_array("email", $tokenData->scope)) {
             $payloadArr['email'] = $user->getPrimaryEmail();
         }
@@ -49,14 +45,15 @@ class TokenBuilder {
             }
         }
 
-        /*if ($tokenData->joinUser !== null) {
-            $payloadArr['alt_sub'] = strval($tokenData->joinUser);
-        }*/
+        if ($jwtCustomizer !== null) {
+            // Add additional custom claims
+            $jwtCustomizer($payloadArr);
+        }
 
         return $payloadArr;
     }
 
-    public function getAccessToken(AccessTokenData $tokenData) {
+    public function getAccessToken(AccessTokenData $tokenData, \Closure $jwtCustomizer = null) {
 
         $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();
 
@@ -67,8 +64,13 @@ class TokenBuilder {
             'sub' => strval($user->id),
             'iat' => intval($tokenData->creationTime),
             'exp' => intval($tokenData->expirationTime),
-            'aud' => $this->getAudience($tokenData)
+            'aud' => $this->getAudience($tokenData),
+            'scope' => implode(' ', $tokenData->scope)
         );
+        if ($jwtCustomizer !== null) {
+            // Add additional custom claims
+            $jwtCustomizer($payload);
+        }
 
         return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
     }
diff --git a/classes/TokenChecker.php b/classes/TokenChecker.php
new file mode 100644
index 0000000000000000000000000000000000000000..370bc8526f6470d145f93e8798ce79d7aed4a74c
--- /dev/null
+++ b/classes/TokenChecker.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace RAP;
+
+use \Firebase\JWT\JWT;
+
+class TokenChecker {
+
+    private $locator;
+
+    public function __construct(Locator $locator) {
+        $this->locator = $locator;
+    }
+
+    public function validateToken(): object {
+        $headers = apache_request_headers();
+
+        if (!isset($headers['Authorization'])) {
+            throw new BadRequestException("Missing Authorization header");
+        }
+
+        $authorizationHeader = explode(" ", $headers['Authorization']);
+        if ($authorizationHeader[0] === "Bearer") {
+            $token = $authorizationHeader[1];
+        } else {
+            throw new BadRequestException("Invalid token type");
+        }
+
+        return $this->attemptJWTTokenValidation($token);
+    }
+
+    private function attemptJWTTokenValidation($jwt): object {
+
+        $jwtParts = explode('.', $jwt);
+        if (count($jwtParts) === 0) {
+            throw new UnauthorizedException("Invalid token");
+        }
+
+        $header = JWT::jsonDecode(JWT::urlsafeB64Decode($jwtParts[0]));
+        if (!isset($header->kid)) {
+            throw new UnauthorizedException("Invalid token: missing kid in header");
+        }
+
+        $keyPair = $this->locator->getJWKSDAO()->getRSAKeyPairById($header->kid);
+        if ($keyPair === null) {
+            throw new UnauthorizedException("Invalid kid: no key found");
+        }
+
+        try {
+            return JWT::decode($jwt, $keyPair->publicKey, [$keyPair->alg]);
+        } catch (\Firebase\JWT\ExpiredException $ex) {
+            throw new UnauthorizedException("Access token is expired");
+        }
+    }
+
+    public function checkScope(object $tokenData, string $desiredScope): void {
+
+        if (!(isset($tokenData->scope))) {
+            throw new UnauthorizedException("Missing 'scope' claim in access token");
+        }
+
+        $scopes = explode(' ', $tokenData->scope);
+
+        foreach ($scopes as $scope) {
+            if ($scope === $desiredScope) {
+                return;
+            }
+        }
+
+        throw new UnauthorizedException("Scope '$desiredScope' is required for performing this action");
+    }
+
+}
diff --git a/classes/UserHandler.php b/classes/UserHandler.php
index 86facfecee0e1e29f45bd79bdb057ef0b9f8b6fe..f744588c663e72c52140bd92468c62f2036ff268 100644
--- a/classes/UserHandler.php
+++ b/classes/UserHandler.php
@@ -71,7 +71,6 @@ class UserHandler {
         // Call Grouper for moving groups and privileges from one user to the other
         if (isset($this->locator->config->gms)) {
 
-            // TODO: change with new GMS
             //create cURL connection
             $conn = curl_init($this->locator->config->gms->joinEndpoint);
 
@@ -81,7 +80,7 @@ class UserHandler {
             curl_setopt($conn, CURLOPT_SSL_VERIFYPEER, true);
             curl_setopt($conn, CURLOPT_FOLLOWLOCATION, 1);
             curl_setopt($conn, CURLOPT_HTTPHEADER, ['Authorization: Bearer '
-                . $this->getJoinIdToken($userId1, $userId2)]);
+                . $this->getJoinAccessToken($userId1, $userId2)]);
 
             //set data to be posted
             curl_setopt($conn, CURLOPT_POST, 1);
@@ -113,19 +112,20 @@ class UserHandler {
         return $user1;
     }
 
-    private function getJoinIdToken(int $userId1, int $userId2): string {
+    private function getJoinAccessToken(int $userId1, int $userId2): string {
 
         $gmsId = $this->locator->config->gms->id;
 
-        $accessToken = new AccessToken();
+        $accessToken = new AccessTokenData();
         $accessToken->clientId = $gmsId;
         $accessToken->userId = $userId1;
-        $accessToken->joinUser = $userId2;
         // shorter expiration
         $accessToken->expirationTime = $accessToken->creationTime + 100;
         $accessToken->scope = ['openid'];
 
-        return $this->locator->getTokenBuilder()->getIdToken($accessToken);
+        return $this->locator->getTokenBuilder()->getAccessToken($accessToken, function(& $jwt) use($userId2) {
+                    $jwt['alt_sub'] = strval($userId2);
+                });
     }
 
 }
diff --git a/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php b/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php
index b63f94ba1d7b6f25bb40e2030b2e5aeba372dead..cbc60f5a1115d837024caa9cfccba4fb50135ffd 100644
--- a/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php
+++ b/classes/datalayer/mysql/MySQLOAuth2ClientDAO.php
@@ -24,7 +24,7 @@ class MySQLOAuth2ClientDAO extends BaseMySQLDAO implements OAuth2ClientDAO {
         return $clients;
     }
 
-    private function getClientsMap(PDO $dbh): array {
+    private function getClientsMap(\PDO $dbh): array {
 
         // Load clients info
         $queryClient = "SELECT id, title, icon, client, secret, redirect_url, scope, home_page, show_in_home FROM oauth2_client";
@@ -50,7 +50,7 @@ class MySQLOAuth2ClientDAO extends BaseMySQLDAO implements OAuth2ClientDAO {
         return $clientsMap;
     }
 
-    private function loadAuthenticationMethods(PDO $dbh, array $clientsMap): void {
+    private function loadAuthenticationMethods(\PDO $dbh, array $clientsMap): void {
 
         $queryAuthNMethods = "SELECT client_id, auth_method FROM oauth2_client_auth_methods";
 
@@ -63,7 +63,7 @@ class MySQLOAuth2ClientDAO extends BaseMySQLDAO implements OAuth2ClientDAO {
         }
     }
 
-    private function loadScopeAudienceMapping(PDO $dbh, array $clientsMap): void {
+    private function loadScopeAudienceMapping(\PDO $dbh, array $clientsMap): void {
 
         $query = "SELECT client_id, scope, audience FROM oauth2_client_scope_audience_mapping";
 
diff --git a/classes/login/LoginHandler.php b/classes/login/LoginHandler.php
index fc2531d186007782c802be6f7075120403c48551..9ebdf218813fc5c4a75c31556d30e562734fbb1c 100644
--- a/classes/login/LoginHandler.php
+++ b/classes/login/LoginHandler.php
@@ -85,7 +85,6 @@ class LoginHandler {
             $action = $session->getAction();
 
             if ($action === 'join') {
-
                 if ($session->getUser()->id !== $user->id) {
                     $user = $this->locator->getUserHandler()->joinUsers($session->getUser(), $user);
                 }
diff --git a/include/front-controller.php b/include/front-controller.php
index a3010bc5fc3aff6a7c258c54cd1e90ee8c1aa286..31ecec6e458aece9f798435de78000d1fbe9b8ee 100644
--- a/include/front-controller.php
+++ b/include/front-controller.php
@@ -49,11 +49,6 @@ Flight::route('/', function() {
             $authPageModel = new \RAP\AuthPageModel($locator, $client);
             renderMainPage($authPageModel);
             break;
-        /*case "admin":
-            $client = new \RAP\InternalClient('admin');
-            $authPageModel = new \RAP\AuthPageModel($locator, $client);
-            renderMainPage($authPageModel);
-            break;*/
         default:
             session_destroy();
             $clients = $locator->getOAuth2ClientDAO()->getOAuth2Clients();
@@ -127,25 +122,8 @@ Flight::route('POST /auth/oauth2/check_token', function() {
 
     global $locator;
 
-    $headers = apache_request_headers();
-
-    if (!isset($headers['Authorization'])) {
-        throw new \RAP\BadRequestException("Missing Authorization header");
-    }
-
-    $authorizationHeader = explode(" ", $headers['Authorization']);
-    if ($authorizationHeader[0] === "Bearer") {
-        $token = $authorizationHeader[1];
-    } else {
-        throw new \RAP\BadRequestException("Invalid token type");
-    }
-
-    if ($token === null) {
-        throw new \RAP\BadRequestException("Access token is required");
-    }
-
     $requestHandler = new \RAP\OAuth2RequestHandler($locator);
-    $result = $requestHandler->handleCheckTokenRequest($token);
+    $result = $requestHandler->handleCheckTokenRequest();
 
     Flight::json($result);
 });
diff --git a/include/rest-web-service.php b/include/rest-web-service.php
index d67366121d637b1656548286b80797f1b2401055..ac213cf92bb7716bd1bcd1f11508744da3f69d5b 100644
--- a/include/rest-web-service.php
+++ b/include/rest-web-service.php
@@ -13,7 +13,8 @@ Flight::route('GET ' . $WS_PREFIX . '/user/@userId', function($userId) {
 
     global $locator;
 
-    $locator->getOAuth2RequestHandler()->validateToken();
+    $token = $locator->getTokenChecker()->validateToken();
+    $locator->getTokenChecker()->checkScope($token, 'read:rap');
 
     $user = $locator->getUserDAO()->findUserById($userId);
     if ($user !== null) {
@@ -31,7 +32,8 @@ Flight::route('GET ' . $WS_PREFIX . '/user', function() {
 
     global $locator;
 
-    $locator->getOAuth2RequestHandler()->validateToken();
+    $token = $locator->getTokenChecker()->validateToken();
+    $locator->getTokenChecker()->checkScope($token, 'read:rap');
 
     $searchText = Flight::request()->query['search'];
     if ($searchText !== null) {
@@ -51,13 +53,14 @@ Flight::route('GET ' . $WS_PREFIX . '/user', function() {
 /**
  * Create new user from identity data. Return the new user encoded in JSON.
  * This can be used to automatically import users without they explicitly
- * register (this is done for INAF eduGAIN users readling directly from LDAP).
+ * register (this is done for INAF eduGAIN users reading directly from LDAP).
  */
 Flight::route('POST ' . $WS_PREFIX . '/user', function() {
 
     global $locator;
 
-    $locator->getOAuth2RequestHandler()->validateToken();
+    $token = $locator->getTokenChecker()->validateToken();
+    $locator->getTokenChecker()->checkScope($token, 'write:rap');
 
     $postData = Flight::request()->data;
 
diff --git a/views/account-management.php b/views/account-management.php
index ab8db20613d18b22f32e65ed57f20d158c57f335..39712aba8c5797b11af5818f15d1232a6d90a8d6 100644
--- a/views/account-management.php
+++ b/views/account-management.php
@@ -20,7 +20,7 @@ include 'include/header.php';
     <div class="col-sm-2">
         <div class="row">
             <div class="col-sm-12">
-                <a class="btn btn-success disabled" id="join-btn" href="<?php echo $contextRoot; ?>?action=join" title="Perform an additional login to join your identities" data-toggle="tooltip" data-placement="bottom">
+                <a class="btn btn-success" id="join-btn" href="<?php echo $contextRoot; ?>?action=join" title="Perform an additional login to join your identities" data-toggle="tooltip" data-placement="bottom">
                     Join with another identity
                 </a>
             </div>