diff --git a/pom.xml b/pom.xml
index cac2861d87d145dfd6b6660da15b11135d4f4467..691c82fb89d727680741201641f18c47e2fb52d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,7 +25,7 @@
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.caosdb</groupId>
   <artifactId>caosdb-server</artifactId>
-  <version>0.5.1-GRPC0.1.0</version>
+  <version>0.5.1-GRPC0.1.1</version>
   <packaging>jar</packaging>
   <name>CaosDB Server</name>
   <scm>
@@ -56,11 +56,6 @@
     </repository>
   </repositories>
   <dependencies>
-    <dependency>
-      <groupId>io.grpcweb</groupId>
-      <artifactId>grpcweb-java</artifactId>
-      <version>0.1-SNAPSHOT</version>
-    </dependency>
     <dependency>
       <groupId>de.timmfitschen</groupId>
       <artifactId>easy-units</artifactId>
diff --git a/src/main/java/org/caosdb/server/database/BackendTransaction.java b/src/main/java/org/caosdb/server/database/BackendTransaction.java
index f42c849c08e982b835df2629c07d70e1df882a8e..1e627ebafa2d273fafbc1762c70877df3f8fc2b5 100644
--- a/src/main/java/org/caosdb/server/database/BackendTransaction.java
+++ b/src/main/java/org/caosdb/server/database/BackendTransaction.java
@@ -45,6 +45,7 @@ import org.caosdb.server.database.backend.implementation.MySQL.MySQLInsertSparse
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLInsertTransactionHistory;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLIsSubType;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLListRoles;
+import org.caosdb.server.database.backend.implementation.MySQL.MySQLListUsers;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRegisterSubDomain;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAll;
 import org.caosdb.server.database.backend.implementation.MySQL.MySQLRetrieveAllUncheckedFiles;
@@ -101,6 +102,7 @@ import org.caosdb.server.database.backend.interfaces.InsertSparseEntityImpl;
 import org.caosdb.server.database.backend.interfaces.InsertTransactionHistoryImpl;
 import org.caosdb.server.database.backend.interfaces.IsSubTypeImpl;
 import org.caosdb.server.database.backend.interfaces.ListRolesImpl;
+import org.caosdb.server.database.backend.interfaces.ListUsersImpl;
 import org.caosdb.server.database.backend.interfaces.RegisterSubDomainImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveAllUncheckedFilesImpl;
@@ -207,6 +209,7 @@ public abstract class BackendTransaction implements Undoable {
       setImpl(InsertEntityDatatypeImpl.class, MySQLInsertEntityDatatype.class);
       setImpl(RetrieveVersionHistoryImpl.class, MySQLRetrieveVersionHistory.class);
       setImpl(SetFileChecksumImpl.class, MySQLSetFileChecksum.class);
+      setImpl(ListUsersImpl.class, MySQLListUsers.class);
     }
   }
 
diff --git a/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java
new file mode 100644
index 0000000000000000000000000000000000000000..625942d3d23d0c4488cdefec76c70ca29a685278
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/implementation/MySQL/MySQLListUsers.java
@@ -0,0 +1,52 @@
+package org.caosdb.server.database.backend.implementation.MySQL;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.LinkedList;
+import java.util.List;
+import org.caosdb.server.accessControl.UserStatus;
+import org.caosdb.server.database.access.Access;
+import org.caosdb.server.database.backend.interfaces.ListUsersImpl;
+import org.caosdb.server.database.exceptions.TransactionException;
+import org.caosdb.server.database.proto.ProtoUser;
+
+public class MySQLListUsers extends MySQLTransaction implements ListUsersImpl {
+
+  public MySQLListUsers(Access access) {
+    super(access);
+  }
+
+  public static final String STMT_LIST_USERS =
+      "SELECT status, realm, name, email, entity FROM user_info";
+
+  @Override
+  public List<ProtoUser> execute() {
+    List<ProtoUser> users = new LinkedList<>();
+    try {
+      final PreparedStatement stmt = prepareStatement(STMT_LIST_USERS);
+      final ResultSet rs = stmt.executeQuery();
+      try {
+        while (rs.next()) {
+          final ProtoUser user = new ProtoUser();
+          user.name = rs.getString("name");
+          user.realm = rs.getString("realm");
+          user.email = rs.getString("email");
+          user.entity = rs.getInt("entity");
+          if (user.entity == 0) {
+            user.entity = null;
+          }
+          user.status = UserStatus.valueOf(rs.getString("status"));
+          users.add(user);
+        }
+      } finally {
+        rs.close();
+      }
+    } catch (final SQLException e) {
+      throw new TransactionException(e);
+    } catch (final ConnectionException e) {
+      throw new TransactionException(e);
+    }
+    return users;
+  }
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java b/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..771fb913cd30dfad4c5d9662dda5c54f65901823
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/interfaces/ListUsersImpl.java
@@ -0,0 +1,9 @@
+package org.caosdb.server.database.backend.interfaces;
+
+import java.util.List;
+import org.caosdb.server.database.proto.ProtoUser;
+
+public interface ListUsersImpl extends BackendTransactionImpl {
+
+  List<ProtoUser> execute();
+}
diff --git a/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java b/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java
new file mode 100644
index 0000000000000000000000000000000000000000..6b3640732ccf884342280178676551432f18bd39
--- /dev/null
+++ b/src/main/java/org/caosdb/server/database/backend/transaction/ListUsers.java
@@ -0,0 +1,21 @@
+package org.caosdb.server.database.backend.transaction;
+
+import java.util.List;
+import org.caosdb.server.database.BackendTransaction;
+import org.caosdb.server.database.backend.interfaces.ListUsersImpl;
+import org.caosdb.server.database.proto.ProtoUser;
+
+public class ListUsers extends BackendTransaction {
+
+  private List<ProtoUser> users;
+
+  @Override
+  protected void execute() {
+    ListUsersImpl t = getImplementation(ListUsersImpl.class);
+    users = t.execute();
+  }
+
+  public List<ProtoUser> getUsers() {
+    return users;
+  }
+}
diff --git a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
index 5987b28936e4ea6b4444fad6fe73fe7bd2a71edc..258cb8ada62ffdb5dd01beae1ceff8639fb6a988 100644
--- a/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
+++ b/src/main/java/org/caosdb/server/grpc/AccessControlManagementServiceImpl.java
@@ -5,16 +5,65 @@ import java.util.List;
 import org.caosdb.api.acm.v1alpha1.AccessControlManagementServiceGrpc.AccessControlManagementServiceImplBase;
 import org.caosdb.api.acm.v1alpha1.CreateSingleRoleRequest;
 import org.caosdb.api.acm.v1alpha1.CreateSingleRoleResponse;
+import org.caosdb.api.acm.v1alpha1.CreateSingleUserRequest;
+import org.caosdb.api.acm.v1alpha1.CreateSingleUserResponse;
 import org.caosdb.api.acm.v1alpha1.ListRolesRequest;
 import org.caosdb.api.acm.v1alpha1.ListRolesResponse;
+import org.caosdb.api.acm.v1alpha1.ListUsersRequest;
+import org.caosdb.api.acm.v1alpha1.ListUsersResponse;
+import org.caosdb.api.acm.v1alpha1.User;
+import org.caosdb.api.acm.v1alpha1.UserStatus;
 import org.caosdb.server.accessControl.Role;
+import org.caosdb.server.database.proto.ProtoUser;
 import org.caosdb.server.transaction.InsertRoleTransaction;
+import org.caosdb.server.transaction.InsertUserTransaction;
 import org.caosdb.server.transaction.ListRolesTransaction;
+import org.caosdb.server.transaction.ListUsersTransaction;
 
 public class AccessControlManagementServiceImpl extends AccessControlManagementServiceImplBase {
 
   /////////////////////////////////// CONVERTERS
 
+  private ProtoUser convert(User user) {
+    ProtoUser result = new ProtoUser();
+    result.realm = user.getRealm();
+    result.name = user.getName();
+    result.email = user.getEmail();
+    result.status =
+        (user.getStatus() == UserStatus.USER_STATUS_ACTIVE
+            ? org.caosdb.server.accessControl.UserStatus.ACTIVE
+            : org.caosdb.server.accessControl.UserStatus.INACTIVE);
+    return result;
+  }
+
+  private ListUsersResponse convertUsers(List<ProtoUser> users) {
+    ListUsersResponse.Builder response = ListUsersResponse.newBuilder();
+    users.forEach(
+        user -> {
+          response.addUsers(convert(user));
+        });
+
+    return response.build();
+  }
+
+  private User.Builder convert(ProtoUser user) {
+    User.Builder result = User.newBuilder();
+    result.setStatus(convert(user.status));
+    result.setRealm(user.realm);
+    result.setName(user.name);
+    if (user.email != null) {
+      result.setEmail(user.email);
+    }
+    if (user.entity != null) {
+      result.setEntityId(Integer.toString(user.entity));
+    }
+    return result;
+  }
+
+  private UserStatus convert(org.caosdb.server.accessControl.UserStatus status) {
+    return UserStatus.valueOf(status.toString());
+  }
+
   private Role convert(org.caosdb.api.acm.v1alpha1.Role role) {
     Role result = new Role();
 
@@ -62,8 +111,41 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
     return CreateSingleRoleResponse.newBuilder().build();
   }
 
+  ////////////////// ... for users
+
+  private ListUsersResponse listUsersTransaction(ListUsersRequest request) throws Exception {
+    ListUsersTransaction transaction = new ListUsersTransaction();
+    transaction.execute();
+    List<ProtoUser> users = transaction.getUsers();
+    return convertUsers(users);
+  }
+
+  private CreateSingleUserResponse createSingleUserTransaction(CreateSingleUserRequest request)
+      throws Exception {
+    ProtoUser user = convert(request.getUser());
+    InsertUserTransaction transaction =
+        new InsertUserTransaction(user, request.getUser().getPassword());
+    transaction.execute();
+
+    return CreateSingleUserResponse.newBuilder().build();
+  }
+
   ///////////////////////////////////// RPC Methods (API)
 
+  @Override
+  public void listUsers(
+      ListUsersRequest request, StreamObserver<ListUsersResponse> responseObserver) {
+    try {
+      final ListUsersResponse response = listUsersTransaction(request);
+      responseObserver.onNext(response);
+      responseObserver.onCompleted();
+
+    } catch (final Exception e) {
+      e.printStackTrace();
+      responseObserver.onError(e);
+    }
+  }
+
   @Override
   public void listRoles(
       ListRolesRequest request, StreamObserver<ListRolesResponse> responseObserver) {
@@ -91,4 +173,18 @@ public class AccessControlManagementServiceImpl extends AccessControlManagementS
       responseObserver.onError(e);
     }
   }
+
+  @Override
+  public void createSingleUser(
+      CreateSingleUserRequest request, StreamObserver<CreateSingleUserResponse> responseObserver) {
+    try {
+      final CreateSingleUserResponse response = createSingleUserTransaction(request);
+      responseObserver.onNext(response);
+      responseObserver.onCompleted();
+
+    } catch (final Exception e) {
+      e.printStackTrace();
+      responseObserver.onError(e);
+    }
+  }
 }
diff --git a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
index 74adce832c7a76af6b8dd25dfcf0e33aaad32225..5078d55d8bb8bae6e38368f9effb74b2d9d6fede 100644
--- a/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
+++ b/src/main/java/org/caosdb/server/grpc/AuthInterceptor.java
@@ -1,7 +1,10 @@
 package org.caosdb.server.grpc;
 
+import static org.caosdb.server.utils.Utils.URLDecodeWithUTF8;
+
 import io.grpc.Context;
 import io.grpc.Contexts;
+import io.grpc.ForwardingServerCall;
 import io.grpc.Metadata;
 import io.grpc.Metadata.Key;
 import io.grpc.ServerCall;
@@ -10,14 +13,21 @@ import io.grpc.ServerCallHandler;
 import io.grpc.ServerInterceptor;
 import io.grpc.Status;
 import java.util.Base64;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
 import org.apache.shiro.SecurityUtils;
 import org.apache.shiro.authc.AuthenticationException;
 import org.apache.shiro.subject.Subject;
 import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.ServerProperties;
 import org.caosdb.server.accessControl.AnonymousAuthenticationToken;
+import org.caosdb.server.accessControl.AuthenticationUtils;
 import org.caosdb.server.accessControl.RealmUsernamePasswordToken;
+import org.caosdb.server.accessControl.SelfValidatingAuthenticationToken;
+import org.caosdb.server.accessControl.SessionToken;
 import org.caosdb.server.accessControl.UserSources;
+import org.caosdb.server.utils.Utils;
+import org.restlet.data.CookieSetting;
 
 /**
  * ServerInterceptor for Authentication. If the authentication succeeds or if the caller is
@@ -26,14 +36,20 @@ import org.caosdb.server.accessControl.UserSources;
  *
  * @author Timm Fitschen <t.fitschen@indiscale.com>
  */
-class AuthInterceptor implements ServerInterceptor {
+public class AuthInterceptor implements ServerInterceptor {
 
-  private static final Key<String> AUTHENTICATION_HEADER =
+  public static final Key<String> AUTHENTICATION_HEADER =
       Key.of("authentication", Metadata.ASCII_STRING_MARSHALLER);
-  private static final Key<String> AUTHORIZATION_HEADER =
+  public static final Key<String> AUTHORIZATION_HEADER =
       Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER);
-  private static final Context.Key<Subject> SUBJECT_KEY = Context.key("subject");
-  private static final String BASIC_SCHEME_PREFIX = "Basic ";
+  public static final Key<String> COOKIE_HEADER =
+      Key.of("Cookie", Metadata.ASCII_STRING_MARSHALLER);
+  public static final Context.Key<Subject> SUBJECT_KEY = Context.key("subject");
+  public static final String BASIC_SCHEME_PREFIX = "Basic ";
+  public static final Pattern SESSION_TOKEN_COOKIE_PREFIX_PATTERN =
+      Pattern.compile("^\\s*" + AuthenticationUtils.SESSION_TOKEN_COOKIE + "\\s*=\\s*");
+  public static final Predicate<String> SESSION_TOKEN_COOKIE_PREFIX_PREDICATE =
+      SESSION_TOKEN_COOKIE_PREFIX_PATTERN.asPredicate();
   /**
    * A no-op listener. This class is used for failed authentications. We couldn't return a null
    * instead because the documentation of the {@link ServerInterceptor} explicitely forbids it.
@@ -73,6 +89,9 @@ class AuthInterceptor implements ServerInterceptor {
     if (authentication == null) {
       authentication = headers.get(AUTHORIZATION_HEADER);
     }
+    if (authentication == null) {
+      authentication = headers.get(COOKIE_HEADER);
+    }
     Status status =
         Status.UNKNOWN.withDescription(
             "An unknown error occured during authentication. Please report a bug.");
@@ -82,15 +101,47 @@ class AuthInterceptor implements ServerInterceptor {
       status = Status.UNAUTHENTICATED.withDescription("Please login.");
     } else if (authentication.startsWith(BASIC_SCHEME_PREFIX)) {
       return basicAuth(authentication.substring(BASIC_SCHEME_PREFIX.length()), call, headers, next);
+    } else if (SESSION_TOKEN_COOKIE_PREFIX_PREDICATE.test(authentication)) {
+      return sessionTokenAuth(
+          SESSION_TOKEN_COOKIE_PREFIX_PATTERN.split(authentication, 2)[1], call, headers, next);
     } else {
-      status =
-          Status.UNAUTHENTICATED.withDescription(
-              "Unsupported authentication scheme: " + authentication.split(" ", 2)[0]);
+      status = Status.UNAUTHENTICATED.withDescription("Unsupported authentication scheme.");
     }
     call.close(status, new Metadata());
     return new NoOpListener<ReqT>();
   }
 
+  /**
+   * Login via AuthenticationToken and add the resulting subject to the call context.
+   *
+   * @see #updateContext(Subject, ServerCall, Metadata, ServerCallHandler) for more information.
+   */
+  private <ReqT, RespT> Listener<ReqT> sessionTokenAuth(
+      String sessionTokenCookie,
+      ServerCall<ReqT, RespT> call,
+      Metadata headers,
+      ServerCallHandler<ReqT, RespT> next) {
+    try {
+      final String tokenString = URLDecodeWithUTF8(sessionTokenCookie.split(";")[0]);
+
+      final Subject subject = sessionTokenAuth(tokenString);
+      return updateContext(subject, call, headers, next);
+    } catch (final AuthenticationException e) {
+      final Status status =
+          Status.UNAUTHENTICATED.withDescription(
+              "Authentication failed. SessionToken was invalid.");
+      call.close(status, new Metadata());
+      return new NoOpListener<ReqT>();
+    }
+  }
+
+  /** Login via AuthenticationToken and return the logged-in subject. */
+  private Subject sessionTokenAuth(String tokenString) {
+    Subject subject = SecurityUtils.getSubject();
+    subject.login(SelfValidatingAuthenticationToken.parse(tokenString));
+    return subject;
+  }
+
   /**
    * Login via username and password with the basic authentication scheme and add the resulting
    * subject to the call context.
@@ -145,6 +196,54 @@ class AuthInterceptor implements ServerInterceptor {
       final ServerCallHandler<ReqT, RespT> next) {
     final Context context = Context.current();
     context.withValue(SUBJECT_KEY, subject);
-    return Contexts.interceptCall(Context.current(), call, headers, next);
+    ServerCall<ReqT, RespT> cookieSetter = new CookieSetter<>(call);
+    return Contexts.interceptCall(context, cookieSetter, headers, next);
+  }
+}
+
+final class CookieSetter<ReqT, RespT>
+    extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
+  private static final Key<String> SET_COOKIE =
+      Key.of("Set-Cookie", Metadata.ASCII_STRING_MARSHALLER);
+
+  protected CookieSetter(ServerCall<ReqT, RespT> delegate) {
+    super(delegate);
+  }
+
+  String getSessionTimeoutSeconds() {
+    int ms =
+        Integer.parseInt(CaosDBServer.getServerProperty(ServerProperties.KEY_SESSION_TIMEOUT_MS));
+    int seconds = (int) Math.floor(ms / 1000);
+    return Integer.toString(seconds);
+  }
+
+  @Override
+  public void sendHeaders(Metadata headers) {
+    setSessionCookies(headers);
+    super.sendHeaders(headers);
+  };
+
+  private void setSessionCookies(Metadata headers) {
+    final Subject subject = SecurityUtils.getSubject();
+    // if authenticated as a normal user: generate and set session cookie.
+    if (subject.isAuthenticated()
+        && subject.getPrincipal() != AnonymousAuthenticationToken.PRINCIPAL) {
+      final SessionToken sessionToken = SessionToken.generate(subject);
+      if (sessionToken != null && sessionToken.isValid()) {
+
+        final CookieSetting sessionTokenCookie =
+            AuthenticationUtils.createSessionTokenCookie(sessionToken);
+        if (sessionTokenCookie != null) {
+          // TODO add "Secure;" to cookie setting
+          headers.put(
+              SET_COOKIE,
+              AuthenticationUtils.SESSION_TOKEN_COOKIE
+                  + "="
+                  + Utils.URLEncodeWithUTF8(sessionToken.toString())
+                  + "; Path=/; HttpOnly; SameSite=Strict; Max-Age="
+                  + getSessionTimeoutSeconds());
+        }
+      }
+    }
   }
 }
diff --git a/src/main/java/org/caosdb/server/grpc/GRPCServer.java b/src/main/java/org/caosdb/server/grpc/GRPCServer.java
index cff13a0e683affc3cb9b641f43532ecc27e20c02..05d4288e961c4e5ce84ba65465ad358ff31d7330 100644
--- a/src/main/java/org/caosdb/server/grpc/GRPCServer.java
+++ b/src/main/java/org/caosdb/server/grpc/GRPCServer.java
@@ -24,9 +24,6 @@ import io.grpc.ServerInterceptors;
 import io.grpc.ServerServiceDefinition;
 import io.grpc.netty.GrpcSslContexts;
 import io.grpc.netty.NettyServerBuilder;
-import io.grpcweb.GrpcPortNumRelay;
-import io.grpcweb.JettyWebserverForGrpcwebTraffic;
-import io.grpcweb.ServiceToClassMapping;
 import io.netty.handler.ssl.ApplicationProtocolConfig;
 import io.netty.handler.ssl.ApplicationProtocolConfig.Protocol;
 import io.netty.handler.ssl.ApplicationProtocolConfig.SelectedListenerFailureBehavior;
@@ -45,10 +42,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 import javax.net.ssl.KeyManagerFactory;
-import org.caosdb.api.acm.v1alpha1.AccessControlManagementServiceGrpc;
-import org.caosdb.api.entity.v1alpha1.EntityTransactionServiceGrpc;
-import org.caosdb.api.entity.v1alpha1.FileTransmissionServiceGrpc;
-import org.caosdb.api.info.v1alpha1.GeneralInfoServiceGrpc;
 import org.caosdb.server.CaosDBServer;
 import org.caosdb.server.ServerProperties;
 import org.slf4j.Logger;
@@ -122,29 +115,20 @@ public class GRPCServer {
   private List<ServerServiceDefinition> getEnabledServices() {
     final List<ServerServiceDefinition> services = new LinkedList<>();
 
-    // Add mapping from the service name to the service class (for the grpc-web proxy)
-    ServiceToClassMapping.put(
-        AccessControlManagementServiceGrpc.SERVICE_NAME, AccessControlManagementServiceGrpc.class);
     final AccessControlManagementServiceImpl accessControlManagementService =
         new AccessControlManagementServiceImpl();
-    services.add(ServerInterceptors.intercept(accessControlManagementService, authInterceptor));
+    services.add(
+        ServerInterceptors.intercept(
+            accessControlManagementService, loggingInterceptor, authInterceptor));
 
-    // Add mapping from the service name to the service class (for the grpc-web proxy)
-    ServiceToClassMapping.put(GeneralInfoServiceGrpc.SERVICE_NAME, GeneralInfoServiceGrpc.class);
     final GeneralInfoServiceImpl generalInfoService = new GeneralInfoServiceImpl();
     services.add(
         ServerInterceptors.intercept(generalInfoService, loggingInterceptor, authInterceptor));
 
-    // Add mapping from the service name to the service class (for the grpc-web proxy)
-    ServiceToClassMapping.put(
-        FileTransmissionServiceGrpc.SERVICE_NAME, FileTransmissionServiceGrpc.class);
     final FileTransmissionServiceImpl fileTransmissionService = new FileTransmissionServiceImpl();
     services.add(
         ServerInterceptors.intercept(fileTransmissionService, loggingInterceptor, authInterceptor));
 
-    // Add mapping from the service name to the service class (for the grpc-web proxy)
-    ServiceToClassMapping.put(
-        EntityTransactionServiceGrpc.SERVICE_NAME, EntityTransactionServiceGrpc.class);
     final EntityTransactionServiceImpl entityTransactionService =
         new EntityTransactionServiceImpl(fileTransmissionService);
     services.add(
@@ -220,12 +204,6 @@ public class GRPCServer {
       server.start();
       logger.info("Started GRPC (HTTP) on port {}", port_http);
 
-      // Start the grpc-web proxy on grpc-web-port.
-      (new JettyWebserverForGrpcwebTraffic(9443)).start();
-
-      // grpc-web proxy needs to know the grpc-port# so it could connect to the grpc service.
-      GrpcPortNumRelay.setGrpcPortNum(port_http);
-
     } else if (!started) {
       logger.warn(
           "No GRPC Server has been started. Please configure {} or {} to do so.",
diff --git a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
index b91b1aa6b31911822ed9df4c1eaffa87374ede82..d63cd60fc94e09bb0230551bf9fd432b9bee82bf 100644
--- a/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
+++ b/src/main/java/org/caosdb/server/transaction/InsertUserTransaction.java
@@ -36,7 +36,7 @@ import org.jdom2.Element;
 
 public class InsertUserTransaction extends AccessControlTransaction {
 
-  ProtoUser user = new ProtoUser();
+  private final ProtoUser user;
   private final String password;
 
   public InsertUserTransaction(
@@ -45,11 +45,16 @@ public class InsertUserTransaction extends AccessControlTransaction {
       final String email,
       final UserStatus status,
       final Integer entity) {
+    this(new ProtoUser(), password);
     this.user.realm = UserSources.getInternalRealm().getName();
     this.user.name = username;
     this.user.email = email;
     this.user.status = status;
     this.user.entity = entity;
+  }
+
+  public InsertUserTransaction(ProtoUser user, String password) {
+    this.user = user;
     this.password = password;
   }
 
diff --git a/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java
new file mode 100644
index 0000000000000000000000000000000000000000..cfbd68d3412eea162bc96582bddc8193a4781c31
--- /dev/null
+++ b/src/main/java/org/caosdb/server/transaction/ListUsersTransaction.java
@@ -0,0 +1,19 @@
+package org.caosdb.server.transaction;
+
+import java.util.List;
+import org.caosdb.server.database.backend.transaction.ListUsers;
+import org.caosdb.server.database.proto.ProtoUser;
+
+public class ListUsersTransaction extends AccessControlTransaction {
+
+  private List<ProtoUser> users = null;
+
+  @Override
+  protected void transaction() throws Exception {
+    users = execute(new ListUsers(), getAccess()).getUsers();
+  }
+
+  public List<ProtoUser> getUsers() {
+    return users;
+  }
+}
diff --git a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
index ca19603230bf6aa989c495fbadabd0989c2a9790..6ec17c6812f62c4ea5205b8efea488f804d8d0db 100644
--- a/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
+++ b/src/test/java/org/caosdb/server/authentication/AuthTokenTest.java
@@ -24,6 +24,7 @@ package org.caosdb.server.authentication;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectReader;
@@ -53,6 +54,7 @@ import org.caosdb.server.database.backend.interfaces.RetrievePasswordValidatorIm
 import org.caosdb.server.database.backend.interfaces.RetrievePermissionRulesImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveRoleImpl;
 import org.caosdb.server.database.backend.interfaces.RetrieveUserImpl;
+import org.caosdb.server.grpc.AuthInterceptor;
 import org.caosdb.server.resource.TestScriptingResource.RetrievePasswordValidator;
 import org.caosdb.server.resource.TestScriptingResource.RetrievePermissionRules;
 import org.caosdb.server.resource.TestScriptingResource.RetrieveRole;
@@ -448,4 +450,10 @@ public class AuthTokenTest {
     assertEquals(9223372036854775000L, config.getExpiresAfter());
     assertEquals(922337203685477000L, config.getReplayTimeout());
   }
+
+  @Test
+  public void testSessionTokenCookiePattern() {
+    String cookie = "SessionToken=%5B%22S%22%22%5D";
+    assertTrue(AuthInterceptor.SESSION_TOKEN_COOKIE_PREFIX_PATTERN.matcher(cookie).find());
+  }
 }