diff --git a/conf/core/authtoken.example.yaml b/conf/core/authtoken.example.yaml
index d0628842f2b6a8c1037156b286f1eab094ce4499..37d11733d7ffee798e0e1491974703201e8b351b 100644
--- a/conf/core/authtoken.example.yaml
+++ b/conf/core/authtoken.example.yaml
@@ -12,7 +12,7 @@
   output:
     file: "authtoken/admin_token_3_attempts.json"
     schedule: "0/10 * * ? * * *"
-  maxAttempts: 3
+  maxReplays: 3
 - roles:
     - administration
   output:
diff --git a/conf/core/server.conf b/conf/core/server.conf
index ed08510ea7187f82b62bc5eba84cd586af1b5706..d30d82b681a185a820783c6c2d8d81a62981eb64 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -123,11 +123,12 @@ ONE_TIME_TOKEN_EXPIRES_MS=604800000
 # Path to config file for one time tokens, for example authtoken.yml.
 AUTHTOKEN_CONFIG=
 
-# Timeout after which a consumed one-time token expires regardless of the
-# maximum of attempts that are allowed for that token. This is only a default
-# value. The actual timeout of tokens can be configured otherwise.
+# Timeout after which a one-time token expires once it has been first consumed,
+# regardless of the maximum of replays that are allowed for that token. This is
+# only a default value. The actual timeout of tokens can be configured
+# otherwise.
 # 30 s
-ONE_TIME_TOKEN_ATTEMPTS_TIMEOUT_MS=30000
+ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS=30000
 
 # The value for the HTTP cache directive "max-age"
 WEBUI_HTTP_HEADER_CACHE_MAX_AGE=28800
diff --git a/src/main/java/caosdb/server/CaosDBServer.java b/src/main/java/caosdb/server/CaosDBServer.java
index 88beecfbc3d437ab75d121c60a8105c2bbe3107a..45da4eb0e655f011e06deede49eec2035745fac1 100644
--- a/src/main/java/caosdb/server/CaosDBServer.java
+++ b/src/main/java/caosdb/server/CaosDBServer.java
@@ -123,6 +123,9 @@ public class CaosDBServer extends Application {
   private static ArrayList<Runnable> preShutdownHooks = new ArrayList<Runnable>();
   private static boolean START_BACKEND = true;
   private static boolean INSECURE = false;
+  public static final String REQUEST_TIME_LOGGER = "REQUEST_TIME_LOGGER";
+  public static final String REQUEST_ERRORS_LOGGER = "REQUEST_ERRORS_LOGGER";
+  private static Scheduler SCHEDULER;
 
   public static String getServerProperty(final String key) {
     return getServerProperties().getProperty(key);
@@ -527,10 +530,6 @@ public class CaosDBServer extends Application {
     }
   }
 
-  public static final String REQUEST_TIME_LOGGER = "REQUEST_TIME_LOGGER";
-  public static final String REQUEST_ERRORS_LOGGER = "REQUEST_ERRORS_LOGGER";
-  private static Scheduler SCHEDULER;
-
   /**
    * Specify the dispatching restlet that maps URIs to their associated resources for processing.
    *
@@ -580,6 +579,7 @@ public class CaosDBServer extends Application {
           private void setSessionCookies(final Response response) {
 
             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);
diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java
index 6ac7f091fd4670c9e16b55f443f59518d69d8a05..29ba470ddbcad0e79db210917234562c4b58620a 100644
--- a/src/main/java/caosdb/server/ServerProperties.java
+++ b/src/main/java/caosdb/server/ServerProperties.java
@@ -89,8 +89,8 @@ public class ServerProperties extends Properties {
 
   public static final String KEY_SESSION_TIMEOUT_MS = "SESSION_TIMEOUT_MS";
   public static final String KEY_ONE_TIME_TOKEN_EXPIRES_MS = "ONE_TIME_TOKEN_EXPIRES_MS";
-  public static final String KEY_ONE_TIME_TOKEN_ATTEMPTS_TIMEOUT_MS =
-      "ONE_TIME_TOKEN_ATTEMPTS_TIMEOUT_MS";
+  public static final String KEY_ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS =
+      "ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS";
 
   public static final String KEY_CACHE_CONF_LOC = "CACHE_CONF_LOC";
   public static final String KEY_CACHE_DISABLE = "CACHE_DISABLE";
diff --git a/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java b/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
index bc78d6df6116c40fc551f53313b11cbf25faff9a..c3576031da2d9a598bd74a07fe12df62ed7de90d 100644
--- a/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
+++ b/src/main/java/caosdb/server/accessControl/AuthenticationUtils.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -124,9 +126,7 @@ public class AuthenticationUtils {
 
     if (token != null && token.isValid()) {
       t = new Timestamp(token.getExpires()).toString().replaceFirst(" ", "T");
-      exp_in_sec = (int) Math.ceil(token.getTimeout() / 1000.0); // new
-      // expiration
-      // time.
+      exp_in_sec = (int) Math.ceil(token.getTimeout() / 1000.0); // new expiration time
       return new CookieSetting(
           0,
           AuthenticationUtils.SESSION_TIMEOUT_COOKIE,
diff --git a/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java b/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
index be557e49c5aef0bc42f548016549c44dc0202e39..abd52d9ed306f43c51d8bf52bdf1c25d86d8454e 100644
--- a/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
+++ b/src/main/java/caosdb/server/accessControl/CaosDBAuthorizingRealm.java
@@ -49,6 +49,7 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
     final SimpleAuthorizationInfo authzInfo = new SimpleAuthorizationInfo();
     Object principal = principals.getPrimaryPrincipal();
 
+    // Add explicitly given roles and permissions.
     if (principal instanceof SelfValidatingAuthenticationToken) {
       Collection<String> sessionPermissions =
           getSessionPermissions((SelfValidatingAuthenticationToken) principal);
@@ -60,8 +61,7 @@ public class CaosDBAuthorizingRealm extends AuthorizingRealm {
       authzInfo.addStringPermissions(sessionPermissions);
     }
 
-    // find all roles which are associated with this principal in this
-    // realm.
+    // Find all roles which are associated with this principal in this realm.
     final Set<String> principalRoles =
         UserSources.resolve((Principal) principals.getPrimaryPrincipal());
     if (principalRoles != null) {
diff --git a/src/main/java/caosdb/server/accessControl/CaosDBRolePermissionResolver.java b/src/main/java/caosdb/server/accessControl/CaosDBRolePermissionResolver.java
index 3350a68700f108e8ed5cf7309e9f305ac0bb669b..ae4c38605f07f5222499e076def3a3d48c31a385 100644
--- a/src/main/java/caosdb/server/accessControl/CaosDBRolePermissionResolver.java
+++ b/src/main/java/caosdb/server/accessControl/CaosDBRolePermissionResolver.java
@@ -33,6 +33,7 @@ import org.apache.shiro.authc.AuthenticationException;
 
 public class CaosDBRolePermissionResolver {
 
+  /** Return CaosPermission with the rules which are associated with the roles. */
   public CaosPermission resolvePermissionsInRole(final Set<String> roles) {
     final HashSet<PermissionRule> rules = new HashSet<PermissionRule>();
     for (final String role : roles) {
diff --git a/src/main/java/caosdb/server/accessControl/Config.java b/src/main/java/caosdb/server/accessControl/Config.java
index d433a1e90df035e805e46cf5452c1330811ce061..27f35f692cad23c2e03e4bab01b90d6e01b5ab97 100644
--- a/src/main/java/caosdb/server/accessControl/Config.java
+++ b/src/main/java/caosdb/server/accessControl/Config.java
@@ -5,9 +5,9 @@ public class Config {
   private String[] roles = {};
   private String purpose = null;
   private OneTimeTokenToFile output = null;
-  private int maxAttempts = 1;
+  private int maxReplays = 1;
   private int timeout = OneTimeAuthenticationToken.DEFAULT_TIMEOUT_MS;
-  private int attemptsTimeout = OneTimeAuthenticationToken.DEFAULT_ATTEMPTS_TIMEOUT_MS;
+  private int replaysTimeout = OneTimeAuthenticationToken.DEFAULT_REPLAYS_TIMEOUT_MS;
   private String name = AnonymousAuthenticationToken.PRINCIPAL.getUsername();
 
   public Config() {}
@@ -28,20 +28,20 @@ public class Config {
     this.timeout = timeout;
   }
 
-  public void setAttemptsTimeoutSeconds(int seconds) {
-    this.setAttemptsTimeout(seconds * 1000);
+  public void setReplaysTimeoutSeconds(int seconds) {
+    this.setReplaysTimeout(seconds * 1000);
   }
 
   public void setExpiresAfterSeconds(int seconds) {
     this.setTimeout(seconds * 1000);
   }
 
-  public void setMaxAttempts(int maxAttempts) {
-    this.maxAttempts = maxAttempts;
+  public void setMaxReplays(int maxReplays) {
+    this.maxReplays = maxReplays;
   }
 
-  public int getMaxAttempts() {
-    return maxAttempts;
+  public int getMaxReplays() {
+    return maxReplays;
   }
 
   public String[] getPermissions() {
@@ -76,11 +76,11 @@ public class Config {
     this.output = output;
   }
 
-  public int getAttemptsTimeout() {
-    return attemptsTimeout;
+  public int getReplaysTimeout() {
+    return replaysTimeout;
   }
 
-  public void setAttemptsTimeout(int attemptsTimeout) {
-    this.attemptsTimeout = attemptsTimeout;
+  public void setReplaysTimeout(int replaysTimeout) {
+    this.replaysTimeout = replaysTimeout;
   }
 }
diff --git a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
index e211668c8a83ae4dc13addc19406fbc486646c31..6929c41cdd25569839a85bd512909c43f6fc683a 100644
--- a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
+++ b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -43,18 +45,18 @@ import org.slf4j.LoggerFactory;
 
 public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToken {
 
-  public static final long DEFAULT_MAX_ATTEMPTS = 1L;
-  public static final int DEFAULT_ATTEMPTS_TIMEOUT_MS =
+  public static final long DEFAULT_MAX_REPLAYS = 1L;
+  public static final int DEFAULT_REPLAYS_TIMEOUT_MS =
       Integer.parseInt(
-          CaosDBServer.getServerProperty(ServerProperties.KEY_ONE_TIME_TOKEN_ATTEMPTS_TIMEOUT_MS));
+          CaosDBServer.getServerProperty(ServerProperties.KEY_ONE_TIME_TOKEN_REPLAYS_TIMEOUT_MS));
   public static final int DEFAULT_TIMEOUT_MS =
       Integer.parseInt(
           CaosDBServer.getServerProperty(ServerProperties.KEY_ONE_TIME_TOKEN_EXPIRES_MS));
   public static final String REALM_NAME = "OneTimeAuthenticationToken"; // TODO move to UserSources
   public static final Logger LOGGER = LoggerFactory.getLogger(OneTimeAuthenticationToken.class);
 
-  private long maxAttempts;
-  private long attemptsTimeout;
+  private long maxReplays;
+  private long replaysTimeout;
 
   public OneTimeAuthenticationToken(
       final Principal principal,
@@ -64,16 +66,16 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
       final String checksum,
       final String[] permissions,
       final String[] roles,
-      final long maxAttempts,
-      final long attemptsTimeout) {
+      final long maxReplays,
+      final long replaysTimeout) {
     super(principal, date, timeout, salt, checksum, permissions, roles);
-    this.attemptsTimeout = attemptsTimeout;
-    this.maxAttempts = maxAttempts;
+    this.replaysTimeout = replaysTimeout;
+    this.maxReplays = maxReplays;
     consume();
   }
 
-  public long getAttemptsTimeout() {
-    return attemptsTimeout;
+  public long getReplaysTimeout() {
+    return replaysTimeout;
   }
 
   public void consume() {
@@ -85,15 +87,15 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
       final long timeout,
       final String[] permissions,
       final String[] roles,
-      final Long maxAttempts,
-      final Long attemptsTimeout) {
+      final Long maxReplays,
+      final Long replaysTimeout) {
     super(
         principal,
         timeout,
         permissions,
         roles,
-        defaultIfNull(maxAttempts, DEFAULT_MAX_ATTEMPTS),
-        defaultIfNull(attemptsTimeout, DEFAULT_ATTEMPTS_TIMEOUT_MS));
+        defaultIfNull(maxReplays, DEFAULT_MAX_REPLAYS),
+        defaultIfNull(replaysTimeout, DEFAULT_REPLAYS_TIMEOUT_MS));
   }
 
   private static final long serialVersionUID = -1072740888045267613L;
@@ -113,10 +115,10 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
     final long timeout = (Long) array[6];
     final String salt = (String) array[7];
     final String checksum = (String) array[8];
-    final long maxAttempts = (Long) array[9];
-    final long attemptsTimeout = (Long) array[10];
+    final long maxReplays = (Long) array[9];
+    final long replaysTimeout = (Long) array[10];
     return new OneTimeAuthenticationToken(
-        principal, date, timeout, salt, checksum, permissions, roles, maxAttempts, attemptsTimeout);
+        principal, date, timeout, salt, checksum, permissions, roles, maxReplays, replaysTimeout);
   }
 
   private static OneTimeAuthenticationToken generate(
@@ -124,11 +126,11 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
       final String[] permissions,
       final String[] roles,
       final long timeout,
-      final long maxAttempts,
-      final long attemptsTimeout) {
+      final long maxReplays,
+      final long replaysTimeout) {
 
     return new OneTimeAuthenticationToken(
-        principal, timeout, permissions, roles, maxAttempts, attemptsTimeout);
+        principal, timeout, permissions, roles, maxReplays, replaysTimeout);
   }
 
   public static List<Config> loadConfig(InputStream input) throws Exception {
@@ -174,8 +176,8 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
         c.getPermissions(),
         c.getRoles(),
         c.getTimeout(),
-        c.getMaxAttempts(),
-        c.getAttemptsTimeout());
+        c.getMaxReplays(),
+        c.getReplaysTimeout());
   }
 
   static Map<String, Config> purposes = new HashMap<>();
@@ -212,8 +214,8 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
   @Override
   protected void setFields(Object[] fields) {
     if (fields.length == 2) {
-      this.maxAttempts = (long) fields[0];
-      this.attemptsTimeout = (long) fields[1];
+      this.maxReplays = (long) fields[0];
+      this.replaysTimeout = (long) fields[1];
     } else {
       throw new InstantiationError("Too few fields.");
     }
@@ -230,8 +232,8 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
         this.salt,
         calcChecksum((Object[]) this.permissions),
         calcChecksum((Object[]) this.roles),
-        this.maxAttempts,
-        this.attemptsTimeout,
+        this.maxReplays,
+        this.replaysTimeout,
         pepper);
   }
 
@@ -248,13 +250,13 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
           this.timeout,
           this.salt,
           this.checksum,
-          this.maxAttempts,
-          this.attemptsTimeout
+          this.maxReplays,
+          this.replaysTimeout
         });
   }
 
-  public long getMaxAttempts() {
-    return maxAttempts;
+  public long getMaxReplays() {
+    return maxReplays;
   }
 
   public static void resetConfig() {
diff --git a/src/main/java/caosdb/server/accessControl/OneTimeTokenConsumedInfo.java b/src/main/java/caosdb/server/accessControl/OneTimeTokenConsumedInfo.java
index a7122c2b28ed38baaf60c8da6b404b1fcf97c349..a8ebd2c807712d8b06ec68771e7abb8082ce0696 100644
--- a/src/main/java/caosdb/server/accessControl/OneTimeTokenConsumedInfo.java
+++ b/src/main/java/caosdb/server/accessControl/OneTimeTokenConsumedInfo.java
@@ -7,6 +7,10 @@ import java.util.List;
 import java.util.Map;
 import org.apache.shiro.authc.AuthenticationException;
 
+/**
+ * Utility class to manage OTTs: mark as consumed, removed expired OTTs, manage maximum number of
+ * replays and replay timeout of tokens.
+ */
 class OneTimeTokenConsumedInfo {
 
   private static Map<String, OneTimeTokenConsumedInfo> consumedOneTimeTokens = new HashMap<>();
@@ -24,6 +28,7 @@ class OneTimeTokenConsumedInfo {
     }
   }
 
+  /** If the token is valid, consume it once and store this information. */
   public static void consume(OneTimeAuthenticationToken oneTimeAuthenticationToken) {
     if (oneTimeAuthenticationToken.isValid()) {
       String key = OneTimeTokenConsumedInfo.getKey(oneTimeAuthenticationToken);
@@ -40,7 +45,7 @@ class OneTimeTokenConsumedInfo {
   }
 
   private OneTimeAuthenticationToken oneTimeAuthenticationToken;
-  private List<Long> attempts = new LinkedList<>();
+  private List<Long> replays = new LinkedList<>();
 
   public OneTimeTokenConsumedInfo(OneTimeAuthenticationToken oneTimeAuthenticationToken) {
     this.oneTimeAuthenticationToken = oneTimeAuthenticationToken;
@@ -50,30 +55,31 @@ class OneTimeTokenConsumedInfo {
     return token.checksum;
   }
 
-  private int getNoOfAttempts() {
-    return attempts.size();
+  private int getNoOfReplays() {
+    return replays.size();
   }
 
-  private long getMaxAttempts() {
-    return oneTimeAuthenticationToken.getMaxAttempts();
+  private long getMaxReplays() {
+    return oneTimeAuthenticationToken.getMaxReplays();
   }
 
-  private long getAttemptTimeout() {
-    if (attempts.size() == 0) {
+  private long getReplayTimeout() {
+    if (replays.size() == 0) {
       return Long.MAX_VALUE;
     }
-    long firstAttemptTime = attempts.get(0);
-    return firstAttemptTime + oneTimeAuthenticationToken.getAttemptsTimeout();
+    long firstReplayTime = replays.get(0);
+    return firstReplayTime + oneTimeAuthenticationToken.getReplaysTimeout();
   }
 
+  /** If there are still replays and time left, increase the replay counter by one. */
   public void consume() {
-    synchronized (attempts) {
-      if (getNoOfAttempts() >= getMaxAttempts()) {
+    synchronized (replays) {
+      if (getNoOfReplays() >= getMaxReplays()) {
         throw new AuthenticationException("One-time token was consumed too often.");
-      } else if (getAttemptTimeout() < System.currentTimeMillis()) {
-        throw new AuthenticationException("One-time token attempts timeout expired.");
+      } else if (getReplayTimeout() < System.currentTimeMillis()) {
+        throw new AuthenticationException("One-time token replays timeout expired.");
       }
-      attempts.add(System.currentTimeMillis());
+      replays.add(System.currentTimeMillis());
     }
   }
 
diff --git a/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java b/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
index a91dcfa15862f94566ee9da53babbf99ba000e53..b4006783a2105a353749edfaa475bde3dae93808 100644
--- a/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
+++ b/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
@@ -49,10 +49,11 @@ public class OneTimeTokenToFile implements Job {
     this.schedule = schedule;
   }
 
+  /** If no schedule was set, immediately write the config to file, else schedule the job. */
   public void init(Config config) throws IOException, SchedulerException {
 
     if (this.schedule != null) {
-      OneTimeAuthenticationToken.generate(config); // test config
+      OneTimeAuthenticationToken.generate(config); // test config, throw away token
       JobDataMap map = new JobDataMap();
       map.put("config", config);
       map.put("file", file);
diff --git a/src/main/java/caosdb/server/accessControl/SelfValidatingAuthenticationToken.java b/src/main/java/caosdb/server/accessControl/SelfValidatingAuthenticationToken.java
index e65b70efb407ea211f9ed0d5f516a7d17c05eb12..684227384da07555055b8fa3ba3e46bcd0fc26ff 100644
--- a/src/main/java/caosdb/server/accessControl/SelfValidatingAuthenticationToken.java
+++ b/src/main/java/caosdb/server/accessControl/SelfValidatingAuthenticationToken.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -28,12 +30,28 @@ import java.util.Collection;
 import org.apache.shiro.authc.AuthenticationToken;
 import org.eclipse.jetty.util.ajax.JSON;
 
+/**
+ * These AuthenticationTokens are characterized by the following properties:
+ *
+ * <ul>
+ *   <li>date: The creation time.
+ *   <li>timeout: How long this token is valud after creation.
+ *   <li>checksum: ? Used for validation, but how?
+ *   <li>salt: Salt for the password checksum, may be used by inheriting classes.
+ *   <li>pepper: A static property, generated when class is loaded and used until the server
+ *       reboots. Hence all tokens of this kkinf invalidate when the server reboots.
+ *
+ * @todo Explain: Checksum
+ * @todo Is this really a pepper in the sense in which it is usually used?
+ */
 public abstract class SelfValidatingAuthenticationToken extends Principal
     implements AuthenticationToken {
 
   protected static final transient String PEPPER = Utils.getSecureFilename(32);
   private static final long serialVersionUID = -7212039848895531161L;
+  // date is the token creation time, in ms since 1970
   protected final long date;
+  // token validity duration
   protected final long timeout;
   protected final String checksum;
   protected final String salt;
@@ -93,6 +111,7 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
     this.checksum = checksum == null && newChecksum ? calcChecksum() : checksum;
   }
 
+  /** Customizable customization method, will be called with the remaining constructor arguments. */
   protected abstract void setFields(Object[] fields);
 
   public SelfValidatingAuthenticationToken(
@@ -120,8 +139,10 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
   @Override
   public abstract String toString();
 
+  /** Implementation specific version of a peppered checksum. */
   public abstract String calcChecksum(String pepper);
 
+  /** No credentials (returns null), since this token is self-validating. */
   @Override
   public Object getCredentials() {
     return null;
@@ -139,6 +160,9 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
     return System.currentTimeMillis() >= getExpires();
   }
 
+  /**
+   * Test if the hash stored in `checksum` is equal to the one calculated using the secret pepper.
+   */
   public boolean isHashValid() {
     final String other = calcChecksum();
     return this.checksum != null && this.checksum.equals(other);
@@ -148,6 +172,7 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
     return !isExpired() && isHashValid();
   }
 
+  /** Return the hash (SHA512) of the stringified arguments. */
   protected static String calcChecksum(final Object... fields) {
     final StringBuilder sb = new StringBuilder();
     for (final Object field : fields) {
@@ -167,6 +192,12 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
     return ret;
   }
 
+  /**
+   * Parse a JSON string and return the generated token. Depending on the first element of the JSON,
+   * this is either (if it is "O") a OneTimeAuthenticationToken or else a SessionToken
+   *
+   * @todo Only allow "O" and "S"?
+   */
   public static SelfValidatingAuthenticationToken parse(String token) {
     Object[] array = (Object[]) JSON.parse(token);
     switch (array[0].toString()) {
@@ -177,6 +208,7 @@ public abstract class SelfValidatingAuthenticationToken extends Principal
     }
   }
 
+  /** No "other" identity, so this returns itself. */
   @Override
   public SelfValidatingAuthenticationToken getPrincipal() {
     return this;
diff --git a/src/main/java/caosdb/server/accessControl/SessionToken.java b/src/main/java/caosdb/server/accessControl/SessionToken.java
index 2e82e3553497e56e4578f45a7768a9f7a5c71b77..d24b4afeb739d764947092783c544168a6c0cf3f 100644
--- a/src/main/java/caosdb/server/accessControl/SessionToken.java
+++ b/src/main/java/caosdb/server/accessControl/SessionToken.java
@@ -4,6 +4,9 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
+ * Copyright (C) 2020 Daniel Hornung <d.hornung@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -27,6 +30,20 @@ import caosdb.server.ServerProperties;
 import org.apache.shiro.subject.Subject;
 import org.eclipse.jetty.util.ajax.JSON;
 
+/**
+ * Session tokens are formatted as JSON arrays with the following elements:
+ *
+ * <ul>
+ *   <li>Anything but "O" (upper-case "o"), preferred is "S".
+ *   <li>Realm
+ *   <li>name within the Realm
+ *   <li>list of roles
+ *   <li>list of permissions
+ *   <li>time of token generation (long, ms since 1970)
+ *   <li>validity duration (long, ms)
+ *   <li>salt
+ *   <li>checksum
+ */
 public class SessionToken extends SelfValidatingAuthenticationToken {
 
   public SessionToken(
@@ -51,6 +68,7 @@ public class SessionToken extends SelfValidatingAuthenticationToken {
   private static final long serialVersionUID = 5887135104218573761L;
 
   public static SessionToken parse(final Object[] array) {
+    // array[0] is not used here, it was already consumed to determine the type of token.
     final Principal principal = new Principal((String) array[1], (String) array[2]);
     final String[] roles = toStringArray((Object[]) array[3]);
     final String[] permissions = toStringArray((Object[]) array[4]);
@@ -81,6 +99,7 @@ public class SessionToken extends SelfValidatingAuthenticationToken {
     return generate((Principal) subject.getPrincipal(), permissions, roles);
   }
 
+  /** Nothing to set in this implemention. */
   @Override
   protected void setFields(Object[] fields) {}
 
diff --git a/src/main/java/caosdb/server/accessControl/UserSources.java b/src/main/java/caosdb/server/accessControl/UserSources.java
index e09f7c242429140400ff9d3946ab88991021202d..2aae14faddb3a35308ba6982a426db5e6e2e9b8f 100644
--- a/src/main/java/caosdb/server/accessControl/UserSources.java
+++ b/src/main/java/caosdb/server/accessControl/UserSources.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
@@ -116,6 +118,8 @@ public class UserSources extends HashMap<String, UserSource> {
     }
   }
 
+  private Ini map = null;
+
   public UserSource put(final UserSource src) {
     if (src.getName() == null) {
       throw new IllegalArgumentException("A user source's name must not be null.");
@@ -131,8 +135,6 @@ public class UserSources extends HashMap<String, UserSource> {
     return instance.put(src);
   }
 
-  private Ini map = null;
-
   public void initMap() {
     this.map = new Ini();
     try (final FileInputStream f =
@@ -147,6 +149,7 @@ public class UserSources extends HashMap<String, UserSource> {
   /**
    * Return the roles of a given user.
    *
+   * @todo Refactor name: resolveRoles(...)?
    * @param realm
    * @param username
    * @return
@@ -194,6 +197,7 @@ public class UserSources extends HashMap<String, UserSource> {
     return instance.map.getSectionProperty(Ini.DEFAULT_SECTION_NAME, KEY_DEFAULT_REALM, "CaosDB");
   }
 
+  // @todo Refactor name: resolveRoles(...)?
   public static Set<String> resolve(final Principal principal) {
     if (AnonymousAuthenticationToken.PRINCIPAL == principal) {
       // anymous has one role
diff --git a/src/main/java/caosdb/server/resource/ScriptingResource.java b/src/main/java/caosdb/server/resource/ScriptingResource.java
index d3827b4544beb16fd9bbaf37dad795be0e36b129..4f9ef958e676a9f7e743d6820b7df7c41120f7db 100644
--- a/src/main/java/caosdb/server/resource/ScriptingResource.java
+++ b/src/main/java/caosdb/server/resource/ScriptingResource.java
@@ -208,6 +208,10 @@ public class ScriptingResource extends AbstractCaosDBServerResource {
     return AuthenticationUtils.isAnonymous(getUser());
   }
 
+  /**
+   * Generate and return a token for the purpose of the given call. If the user is not anonymous and
+   * the call is not configured to be called by everyone, a SessionToken is returned instead.
+   */
   public Object generateAuthToken(String call) {
     String purpose = "scripting:" + call;
     Object authtoken = OneTimeAuthenticationToken.generateForPurpose(purpose, getUser());
diff --git a/src/main/java/caosdb/server/transaction/Transaction.java b/src/main/java/caosdb/server/transaction/Transaction.java
index 99161894234214bff84d67fe6ded0af1d2ae4767..d2c16438b5c071fec72b11b600604b46c4b2da30 100644
--- a/src/main/java/caosdb/server/transaction/Transaction.java
+++ b/src/main/java/caosdb/server/transaction/Transaction.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
diff --git a/src/main/java/caosdb/server/utils/Initialization.java b/src/main/java/caosdb/server/utils/Initialization.java
index e6d0662e525d920a5496a042b04213c86c5e8c0a..9f77fcf2992c0e7fb8de38ee334bebe8c93ef3d8 100644
--- a/src/main/java/caosdb/server/utils/Initialization.java
+++ b/src/main/java/caosdb/server/utils/Initialization.java
@@ -4,6 +4,8 @@
  *
  * Copyright (C) 2018 Research Group Biomedical Physics,
  * Max-Planck-Institute for Dynamics and Self-Organization Göttingen
+ * Copyright (C) 2020 Indiscale GmbH <info@indiscale.com>
+ * Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Affero General Public License as
diff --git a/src/main/java/caosdb/server/utils/ServerMessages.java b/src/main/java/caosdb/server/utils/ServerMessages.java
index a77ad38c121ccf43b8042f83312767caaf3922ca..29983924246f5bc5987ffdbfeaf949d0946c7f16 100644
--- a/src/main/java/caosdb/server/utils/ServerMessages.java
+++ b/src/main/java/caosdb/server/utils/ServerMessages.java
@@ -26,9 +26,6 @@ import caosdb.server.entity.Message.MessageType;
 
 public class ServerMessages {
 
-  public static final Message UNAUTHENTICATED =
-      new Message(MessageType.Error, 401, "Sign up, please!");
-
   public static final Message ENTITY_HAS_BEEN_DELETED_SUCCESSFULLY =
       new Message(MessageType.Info, 10, "This entity has been deleted successfully.");
 
@@ -258,6 +255,9 @@ public class ServerMessages {
           0,
           "User has been activated. You can now log in with your username and password.");
 
+  public static final Message UNAUTHENTICATED =
+      new Message(MessageType.Error, 401, "Sign in, please.");
+
   public static final Message AUTHORIZATION_ERROR =
       new Message(MessageType.Error, 403, "You are not allowed to do this.");
 
diff --git a/src/main/java/caosdb/server/utils/Utils.java b/src/main/java/caosdb/server/utils/Utils.java
index d5bb0903de581597b23261e0bb87caa1e9ea7829..085be51d0929ce76d10ed5e4e8c32696e0b48476 100644
--- a/src/main/java/caosdb/server/utils/Utils.java
+++ b/src/main/java/caosdb/server/utils/Utils.java
@@ -187,8 +187,8 @@ public class Utils {
   /**
    * Generate a secure filename (base32 letters and numbers).
    *
-   * <p>Very similar to getUID, but uses cryptographic random number instead, also also nicely
-   * formats the resulting string.
+   * <p>Very similar to getUID, but uses cryptographic random number instead, also nicely formats
+   * the resulting string.
    *
    * @param byteSize How many bytes of random bits shall be generated.
    * @return The filename as a String.
diff --git a/src/test/java/caosdb/server/authentication/AuthTokenTest.java b/src/test/java/caosdb/server/authentication/AuthTokenTest.java
index b8b1da51cc78202f2e487662bc6a8260df4fa61b..7fb22fe96fb6d8140a9834d5cc2061d48bb8d637 100644
--- a/src/test/java/caosdb/server/authentication/AuthTokenTest.java
+++ b/src/test/java/caosdb/server/authentication/AuthTokenTest.java
@@ -161,7 +161,7 @@ public class AuthTokenTest {
             new String[] {"roles"},
             1L,
             3000L);
-    Assert.assertEquals(1L, t1.getMaxAttempts());
+    Assert.assertEquals(1L, t1.getMaxReplays());
     Assert.assertFalse(t1.isExpired());
     Assert.assertTrue(t1.isHashValid());
     Assert.assertTrue(t1.isValid());
@@ -172,7 +172,7 @@ public class AuthTokenTest {
     Assert.assertEquals(t1, parsed);
     Assert.assertEquals(serialized, parsed.toString());
 
-    Assert.assertEquals(1L, t1.getMaxAttempts());
+    Assert.assertEquals(1L, t1.getMaxReplays());
     Assert.assertFalse(parsed.isExpired());
     Assert.assertTrue(parsed.isHashValid());
     Assert.assertTrue(parsed.isValid());
@@ -263,7 +263,7 @@ public class AuthTokenTest {
         Integer.parseInt(
             CaosDBServer.getServerProperty(ServerProperties.KEY_ONE_TIME_TOKEN_EXPIRES_MS)),
         config.getTimeout());
-    Assert.assertEquals(1, config.getMaxAttempts());
+    Assert.assertEquals(1, config.getMaxReplays());
     Assert.assertNull("no purpose", config.getPurpose());
 
     Assert.assertArrayEquals("no permissions", new String[] {}, config.getPermissions());
@@ -277,7 +277,7 @@ public class AuthTokenTest {
     testYaml.append("purpose: test purpose 1\n");
     testYaml.append("roles: [ role1, \"role2\"]\n");
     testYaml.append("expiresAfterSeconds: 10\n");
-    testYaml.append("maxAttempts: 3\n");
+    testYaml.append("maxReplays: 3\n");
     testYaml.append("permissions:\n");
     testYaml.append("  - permission1\n");
     testYaml.append("  - 'permission2'\n");
@@ -290,7 +290,7 @@ public class AuthTokenTest {
     Assert.assertTrue("parsing to Config object", map.get("test purpose 1") instanceof Config);
     Config config = map.get("test purpose 1");
     Assert.assertEquals(10000, config.getTimeout());
-    Assert.assertEquals(3, config.getMaxAttempts());
+    Assert.assertEquals(3, config.getMaxReplays());
 
     Assert.assertArrayEquals(
         "permissions parsed",