diff --git a/.gitignore b/.gitignore
index 06c8c148f1e9f45493f574a11c6789398246defd..ab11f8441c1690ede967897c0e64745f9ad30f86 100644
--- a/.gitignore
+++ b/.gitignore
@@ -29,6 +29,7 @@ log/
 OUTBOX
 ConsistencyTest.xml
 testlog/
+authtoken/
 
 # python
 __pycache__
diff --git a/conf/core/authtoken.example.yaml b/conf/core/authtoken.example.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..17c903a3a18b29b79720d4f9de8aade8ecfed2cc
--- /dev/null
+++ b/conf/core/authtoken.example.yaml
@@ -0,0 +1,25 @@
+- purpose: scripting:administration/diagnostics.py
+  roles:
+    - administration
+- roles:
+    - administration
+  output:
+    file: "authtoken/admin_token.json"
+    schedule: "0/10 * * ? * * *"
+- roles:
+    - administration
+  output:
+    file: "authtoken/admin_token_3_attempts.json"
+    schedule: "0/10 * * ? * * *"
+  maxAttempts: 3
+- roles:
+    - administration
+  output:
+    file: "authtoken/admin_token_expired.json"
+  expiresAfterSeconds: 0
+- roles:
+    - administration
+  output:
+    file: "authtoken/admin_token_crud.json"
+    schedule: "0/10 * * ? * * *"
+
diff --git a/conf/core/server.conf b/conf/core/server.conf
index 4c64ace935318b0bb8dc3ac13e16c9c4d7eb7c1c..34d688b69ea5f00bc37348110ed85d57b715b7dd 100644
--- a/conf/core/server.conf
+++ b/conf/core/server.conf
@@ -120,6 +120,9 @@ SESSION_TIMEOUT_MS=600000
 # 7days
 ONE_TIME_TOKEN_EXPIRES_MS=604800000
 
+# Path to config file for one time tokens.
+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 each token can be configured otherwise.
diff --git a/src/main/java/caosdb/server/ServerProperties.java b/src/main/java/caosdb/server/ServerProperties.java
index f29edeb1832014db1933dd85744dd22611cee847..6ac7f091fd4670c9e16b55f443f59518d69d8a05 100644
--- a/src/main/java/caosdb/server/ServerProperties.java
+++ b/src/main/java/caosdb/server/ServerProperties.java
@@ -131,6 +131,7 @@ public class ServerProperties extends Properties {
   public static final String KEY_TIMEZONE = "TIMEZONE";
   public static final String KEY_WEBUI_HTTP_HEADER_CACHE_MAX_AGE =
       "WEBUI_HTTP_HEADER_CACHE_MAX_AGE";
+  public static final String KEY_AUTHTOKEN_CONFIG = "AUTHTOKEN_CONFIG";
 
   /**
    * Read the config files and initialize the server properties.
diff --git a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
index 464ae410f353d4c2890386a0d074fc19ed904c83..e211668c8a83ae4dc13addc19406fbc486646c31 100644
--- a/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
+++ b/src/main/java/caosdb/server/accessControl/OneTimeAuthenticationToken.java
@@ -28,7 +28,6 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.ObjectReader;
 import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
@@ -39,6 +38,8 @@ import java.util.Map;
 import org.apache.shiro.subject.Subject;
 import org.eclipse.jetty.util.ajax.JSON;
 import org.quartz.SchedulerException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToken {
 
@@ -50,6 +51,7 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
       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;
@@ -198,11 +200,12 @@ public class OneTimeAuthenticationToken extends SelfValidatingAuthenticationToke
 
   public static void initConfig() throws Exception {
     resetConfig();
-    try (FileInputStream f = new FileInputStream("conf/ext/authtoken.yaml")) {
+    try (FileInputStream f =
+        new FileInputStream(
+            CaosDBServer.getServerProperty(ServerProperties.KEY_AUTHTOKEN_CONFIG))) {
       initConfig(f);
-    } catch (FileNotFoundException e) {
-      // TODO log and use default config
-      e.printStackTrace();
+    } catch (IOException e) {
+      LOGGER.error("Could not load the auth token configuration", e);
     }
   }
 
diff --git a/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java b/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
index ea56899a827289e1b9b440ca67a26e414d50221a..a91dcfa15862f94566ee9da53babbf99ba000e53 100644
--- a/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
+++ b/src/main/java/caosdb/server/accessControl/OneTimeTokenToFile.java
@@ -1,6 +1,8 @@
 package caosdb.server.accessControl;
 
 import caosdb.server.CaosDBServer;
+import com.google.common.io.Files;
+import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
 import org.quartz.CronScheduleBuilder;
@@ -21,8 +23,13 @@ public class OneTimeTokenToFile implements Job {
   public OneTimeTokenToFile() {}
 
   public static void output(OneTimeAuthenticationToken t, String file) throws IOException {
+    output(t, new File(file));
+  }
+
+  public static void output(OneTimeAuthenticationToken t, File file) throws IOException {
+    Files.createParentDirs(file);
     try (PrintWriter writer = new PrintWriter(file, "utf-8")) {
-      writer.println(t.toString());
+      writer.print(t.toString());
     }
   }