diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0b192d108f201552238b449dba5eea5733f66bc4..0a983723ca8f9be6d27c395f85d445a44d7a317e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -65,7 +65,7 @@ build_extension:
     - cd vollt
     - gradle jar
     - cd ../private-rows-extension
-    - mvn clean package
+    - JAVA_HOME=/usr/lib/jvm/java-14-openjdk-amd64 mvn clean package
   artifacts:
     paths:
       - private-rows-extension/target/private-rows-extension-*.jar
diff --git a/Dockerfile b/Dockerfile
index a67bc7b514c53c447a3cf296bdceffd0f7e3acd5..70f0301442aeb24fa5da21743869b02f549b084e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,2 +1,2 @@
-FROM tomcat:9-jdk8
+FROM tomcat:9-jdk14
 ADD war/tap.war /usr/local/tomcat/webapps/
diff --git a/README.md b/README.md
index 77c3eadbcecceef67e16c93c52f36e165c76f7d0..65ca9a3787cc9ae249eaa7e6b353f6f92e06b14f 100644
--- a/README.md
+++ b/README.md
@@ -11,17 +11,15 @@ If you experience issues with the pull command use this setting in `/etc/docker/
 
 ## Demo
 
-    ./call-tap.sh badtoken
+Login to [RAP token issuer](https://sso.ia2.inaf.it/rap-ia2) to retrieve a valid JWT (select GMS service from dropdown menu).
 
-Only free records (2 rows)
+    ./call-tap.sh
 
-    ./call-tap.sh token1
+Returns only free records (2 rows)
 
-Free records + records associated with group1 and group2 (6 rows)
+    ./call-tap.sh <JWT>
 
-    ./call-tap.sh token2
-
-Free records + records associated with group2 (4 rows)
+Returns records associated with user groups retrieved from GMS
 
 ## Shutdown
 
diff --git a/call-tap.sh b/call-tap.sh
index f2583a02991e619914ff8e2aa31d286577556d0b..280a3b9fbd603c80d39324c608e2e33ce5922f5d 100755
--- a/call-tap.sh
+++ b/call-tap.sh
@@ -1,15 +1,20 @@
 #!/bin/bash
 
-if [ "$#" -ne 1 ]; then
-    echo "Usage: $0 <token>"
-    exit 1
+if [ "$#" -eq 1 ]; then
+    curl -s -XPOST \
+            -H "Authorization: Bearer $1" \
+            -F 'REQUEST=doQuery' \
+            -F 'LANG=ADQL' \
+            -F 'FORMAT=text/csv' \
+            -F 'PHASE=RUN' \
+            -F "QUERY=SELECT * from demo.private_rows;" \
+            http://localhost:8080/tap/sync
+else
+    curl -s -XPOST \
+            -F 'REQUEST=doQuery' \
+            -F 'LANG=ADQL' \
+            -F 'FORMAT=text/csv' \
+            -F 'PHASE=RUN' \
+            -F "QUERY=SELECT * from demo.private_rows;" \
+            http://localhost:8080/tap/sync
 fi
-
-curl -s -XPOST \
-        -H "Authorization: Bearer $1" \
-        -F 'REQUEST=doQuery' \
-        -F 'LANG=ADQL' \
-        -F 'FORMAT=text/csv' \
-        -F 'PHASE=RUN' \
-        -F "QUERY=SELECT * from demo.private_rows;" \
-        http://localhost:8080/tap/sync
diff --git a/database/01-init.sql b/database/01-init.sql
index cbc3c02c64c8657acbbd2c36801b067e3d060f83..c3533e1d47ba7a1a6d02f91c4e805b58dd76b4b1 100644
--- a/database/01-init.sql
+++ b/database/01-init.sql
@@ -8,10 +8,10 @@ CREATE TABLE demo.private_rows (
 
 INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value1', 'FREE', '');
 INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value2', 'FREE', '');
-INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value3', 'PRIV', 'group1');
-INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value4', 'PRIV', 'group1');
-INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value5', 'PRIV', 'group2');
-INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value6', 'PRIV', 'group2');
+INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value3', 'PRIV', 'VLKB.group1');
+INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value4', 'PRIV', 'VLKB.group1');
+INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value5', 'PRIV', 'VLKB.group2');
+INSERT INTO demo.private_rows (value, policy, "group") VALUES ('value6', 'PRIV', 'VLKB.group2');
 
 -- WARNING: always create a new role because Row Level Security doesn't work for table owner
 CREATE ROLE tap WITH LOGIN PASSWORD 'demo';
diff --git a/private-rows-extension/Dockerfile-build-env b/private-rows-extension/Dockerfile-build-env
index 7ca2bf24301d62d3d117d7c61e00a163c2d9b1bc..d13e628da32a0196a98fbed484953e078385e6f4 100644
--- a/private-rows-extension/Dockerfile-build-env
+++ b/private-rows-extension/Dockerfile-build-env
@@ -1,2 +1,2 @@
 FROM gradle:jdk8
-RUN apt-get update && apt install -y maven
+RUN apt-get update && apt install -y openjdk-14-jdk maven
diff --git a/private-rows-extension/nb-configuration.xml b/private-rows-extension/nb-configuration.xml
deleted file mode 100644
index a65c4514a3d97bc9851e99794ec52135faa6fef5..0000000000000000000000000000000000000000
--- a/private-rows-extension/nb-configuration.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project-shared-configuration>
-    <!--
-This file contains additional configuration written by modules in the NetBeans IDE.
-The configuration is intended to be shared among all the users of project and
-therefore it is assumed to be part of version control checkout.
-Without this configuration present, some functionality in the IDE may be limited or fail altogether.
--->
-    <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
-        <!--
-Properties that influence various parts of the IDE, especially code formatting and the like. 
-You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
-That way multiple projects can share the same settings (useful for formatting rules for example).
-Any value defined here will override the pom.xml file value but is only applicable to the current project.
--->
-        <netbeans.hint.jdkPlatform>JDK_1.8</netbeans.hint.jdkPlatform>
-    </properties>
-</project-shared-configuration>
diff --git a/private-rows-extension/pom.xml b/private-rows-extension/pom.xml
index 2003e6bea9d7d10be585363624304e7c0c2a4983..12f0e4e030f55262ab5d0809e9c4321af1cf7770 100644
--- a/private-rows-extension/pom.xml
+++ b/private-rows-extension/pom.xml
@@ -7,8 +7,8 @@
     <packaging>jar</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-        <maven.compiler.source>8</maven.compiler.source>
-        <maven.compiler.target>8</maven.compiler.target>
+        <maven.compiler.source>14</maven.compiler.source>
+        <maven.compiler.target>14</maven.compiler.target>
     </properties>
     <dependencies>
         <dependency>
@@ -18,6 +18,11 @@
             <scope>system</scope>
             <systemPath>${basedir}/../vollt/build/libs/vollt.jar</systemPath>
         </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>auth-lib</artifactId>
+            <version>2.0.0-SNAPSHOT</version>
+        </dependency>
         <dependency>
             <groupId>javax</groupId>
             <artifactId>javaee-web-api</artifactId>
@@ -55,6 +60,20 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <version>2.22.2</version>
             </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 </project>
\ No newline at end of file
diff --git a/private-rows-extension/src/main/java/it/inaf/ia2/vollt/CustomUserIdentifier.java b/private-rows-extension/src/main/java/it/inaf/ia2/vollt/IA2UserIdentifier.java
similarity index 62%
rename from private-rows-extension/src/main/java/it/inaf/ia2/vollt/CustomUserIdentifier.java
rename to private-rows-extension/src/main/java/it/inaf/ia2/vollt/IA2UserIdentifier.java
index 68aa8a9b957c99a9765574dac23dd78189871c4e..5c19b7cea3b76dc3d2a8686b9123f12779e74645 100644
--- a/private-rows-extension/src/main/java/it/inaf/ia2/vollt/CustomUserIdentifier.java
+++ b/private-rows-extension/src/main/java/it/inaf/ia2/vollt/IA2UserIdentifier.java
@@ -1,5 +1,8 @@
 package it.inaf.ia2.vollt;
 
+import it.inaf.ia2.aa.ServiceLocator;
+import it.inaf.ia2.aa.data.User;
+import it.inaf.ia2.aa.jwt.InvalidTokenException;
 import java.util.Arrays;
 import java.util.Map;
 import javax.servlet.http.HttpServletRequest;
@@ -8,7 +11,7 @@ import uws.job.user.JobOwner;
 import uws.service.UWSUrl;
 import uws.service.UserIdentifier;
 
-public class CustomUserIdentifier implements UserIdentifier {
+public class IA2UserIdentifier implements UserIdentifier {
 
     @Override
     public JobOwner extractUserId(UWSUrl urlInterpreter, HttpServletRequest request) throws UWSException {
@@ -22,7 +25,7 @@ public class CustomUserIdentifier implements UserIdentifier {
             }
         }
 
-        return getFakeUser(token);
+        return getUser(token);
     }
 
     @Override
@@ -30,14 +33,14 @@ public class CustomUserIdentifier implements UserIdentifier {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    private CustomJobOwner getFakeUser(String token) {
+    private CustomJobOwner getUser(String token) throws UWSException {
 
         if (token != null) {
-            switch (token) {
-                case "token1":
-                    return new CustomJobOwner("user1", Arrays.asList("group1", "group2"));
-                case "token2":
-                    return new CustomJobOwner("user2", Arrays.asList("group2"));
+            try {
+                User user = ServiceLocator.getInstance().getUserManager().getUserFromAccessToken(token);
+                return new CustomJobOwner(user.getName(), user.getGroups());
+            } catch (InvalidTokenException ex) {
+                throw new UWSException(401, "Invalid token");
             }
         }
 
diff --git a/private-rows-extension/src/main/resources/auth.properties b/private-rows-extension/src/main/resources/auth.properties
new file mode 100644
index 0000000000000000000000000000000000000000..d549b7603d9fc24598a6a779ee9a3c6d5002df51
--- /dev/null
+++ b/private-rows-extension/src/main/resources/auth.properties
@@ -0,0 +1,4 @@
+rap_uri=https://sso.ia2.inaf.it/rap-ia2
+gms_uri=https://sso.ia2.inaf.it/gms
+groups_autoload=true
+scope=openid read:gms read:rap
\ No newline at end of file
diff --git a/war/fill-war.sh b/war/fill-war.sh
index 059a06d0298d66643d7b30fda9a9d2e739e346f2..66267ee6177db7c6136169fd53680828f04143aa 100755
--- a/war/fill-war.sh
+++ b/war/fill-war.sh
@@ -6,7 +6,7 @@ rm tap.war
 unzip "$base_war" -d vollt
 cp tap.properties vollt/WEB-INF/classes/tap.properties
 cp web.xml vollt/WEB-INF/
-cp ../private-rows-extension/target/private-rows-extension-*.jar vollt/WEB-INF/lib/
+cp ../private-rows-extension/target/private-rows-extension-*-shaded.jar vollt/WEB-INF/lib/
 rm vollt/WEB-INF/lib/postgresql-9*.jar
 cp postgresql-*.jar vollt/WEB-INF/lib/
 cd vollt
diff --git a/war/tap.properties b/war/tap.properties
index 04ba43399a14dd6e35478d30ee68291670618ef8..1f662972c1df4096b994c216f50a2e83627d0549 100644
--- a/war/tap.properties
+++ b/war/tap.properties
@@ -8,5 +8,5 @@ metadata = db
 file_manager = local
 file_root_path = /tmp
 TAP_SCHEMA = TAP_SCHEMA
-user_identifier={it.inaf.ia2.vollt.CustomUserIdentifier}
+user_identifier={it.inaf.ia2.vollt.IA2UserIdentifier}
 query_executor={it.inaf.ia2.vollt.PrivateRowsQueryExecutor}