diff --git a/classes/IdTokenBuilder.php b/classes/IdTokenBuilder.php
index 456a678910fa15f8e8fc32f01faa5b7179f69745..0f9ea1719f69b34ccc9e03630e09ce24db52b186 100644
--- a/classes/IdTokenBuilder.php
+++ b/classes/IdTokenBuilder.php
@@ -22,7 +22,7 @@ class IdTokenBuilder {
     }
 
     private function createPayloadArray(AccessToken $accessToken, string $nonce = null) {
-        
+
         $user = $this->locator->getUserDAO()->findUserById($accessToken->userId);
 
         $payloadArr = array(
@@ -56,4 +56,27 @@ class IdTokenBuilder {
         return $payloadArr;
     }
 
+    /**
+     * @param int $lifespan in hours
+     * @param string $audit target service
+     */
+    public function generateNewToken(int $lifespan, string $audit) {
+        $keyPair = $this->locator->getJWKSDAO()->getNewestKeyPair();
+
+        $user = $this->locator->getSession()->getUser();
+
+        $iat = time();
+        $exp = $iat + $lifespan * 3600;
+
+        $payload = array(
+            'iss' => $this->locator->config->jwtIssuer,
+            'sub' => strval($user->id),
+            'iat' => $iat,
+            'exp' => $exp,
+            'aud' => $audit
+        );
+
+        return JWT::encode($payload, $keyPair->privateKey, $keyPair->alg, $keyPair->keyId);
+    }
+
 }
diff --git a/config-example.json b/config-example.json
index db809602b8f8d1ca213b1fa3c1fc7dbc4c2e7249..7d8a68604885b8122eec2ff21755266ad9c239f6 100644
--- a/config-example.json
+++ b/config-example.json
@@ -47,5 +47,12 @@
     "gms": {
         "id": "gms",
         "joinEndpoint": "http://localhost:8082/gms/ws/jwt/join"
+    },
+    "tokenIssuer": {
+        "services": [{
+                "id": "fileserver",
+                "label": "File Server"
+            }],
+        "lifespan": [1, 6, 12, 24]
     }
 }
diff --git a/css/style.css b/css/style.css
index 021218b894fe81a217d6e3e322c0b01bc07b31b1..732e5dc44724bb948980905510b9639dad321d7b 100644
--- a/css/style.css
+++ b/css/style.css
@@ -188,4 +188,8 @@ body {
 .service-logo {
     padding-right: 10px;
     max-height: 50px;
+}
+
+#token-issuer-btn {
+    margin-top: 20px;
 }
\ No newline at end of file
diff --git a/include/front-controller.php b/include/front-controller.php
index 88010c7f41d835f1d8dda52d1b7b24c2afe5b628..4df876eabd279b985dca7f5487f90e3936332578 100644
--- a/include/front-controller.php
+++ b/include/front-controller.php
@@ -349,4 +349,54 @@ Flight::route('GET /account', function () {
     }
 });
 
+Flight::route('GET /token-issuer', function () {
+
+    session_start();
+
+    if (empty($_SESSION['csrf_token'])) {
+        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
+    }
+    $csrfToken = $_SESSION['csrf_token'];
+
+    global $locator;
+
+    $user = $locator->getSession()->getUser();
+    $config = $locator->config->tokenIssuer;
+
+    if ($user === null) {
+        Flight::redirect('/');
+    } else {
+        $admin = $locator->getUserDAO()->isAdmin($user->id);
+        Flight::render('token-issuer.php', array('title' => 'RAP Token Issuer',
+            'version' => $locator->getVersion(), 'session' => $locator->getSession(),
+            'config' => $config, 'csrfToken' => $csrfToken,
+            'contextRoot' => $locator->config->contextRoot));
+    }
+});
+
+Flight::route('POST /token-issuer', function () {
+
+    session_start();
+    global $locator;
+
+    if (empty($_POST['csrf_token']) || !(hash_equals($_SESSION['csrf_token'], $_POST['csrf_token']))) {
+        throw new \RAP\UnauthorizedException("Invalid CSRF token");
+    }
+    if ($locator->getSession()->getUser() === null) {
+        throw new \RAP\UnauthorizedException("You must be registered to perform this action");
+    }
+
+    $postData = Flight::request()->data;
+    if (!isset($postData['lifespan']) || !isset($postData['audit'])) {
+        throw new \RAP\BadRequestException("Missing form parameter");
+    }
+
+    $tokenBuilder = $locator->getIdTokenBuilder();
+    $token = $tokenBuilder->generateNewToken($postData['lifespan'], $postData['audit']);
+
+    header('Content-Type: text/plain');
+    header("Content-disposition: attachment; filename=\"token.txt\"");
+    echo $token;
+});
+
 include 'admin.php';
diff --git a/js/index.js b/js/index.js
index d6b1003f08078743aa885f47b88e42dcedc9a813..78e521aeeba5d0a83f871c3e23452273652fd5d5 100644
--- a/js/index.js
+++ b/js/index.js
@@ -28,6 +28,7 @@
     function loadTooltips() {
         $('.primary-identity-icon').tooltip();
         $('#join-btn').tooltip();
+        $('#token-issuer-btn').tooltip();
     }
 
     // When the document is loaded
diff --git a/views/account-management.php b/views/account-management.php
index 5cb798c9a1285bccc9cb2f7bb5a7f5ab968029fa..03e9e4c19a38cc97b8867c1d5005d9b92653c849 100644
--- a/views/account-management.php
+++ b/views/account-management.php
@@ -17,10 +17,21 @@ include 'include/header.php';
             </div>
         </div>
     </div>
-    <div class="col-sm-2 text-center">
-        <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 class="col-sm-2">
+        <div class="row">
+            <div class="col-sm-12">
+                <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>
+        </div>
+        <div class="row">
+            <div class="col-sm-12">
+                <a class="btn btn-default" id="token-issuer-btn" href="<?php echo $contextRoot; ?>/token-issuer" title="Generate tokens for CLI" data-toggle="tooltip" data-placement="bottom">
+                    Token issuer
+                </a>
+            </div>
+        </div>
     </div>
     <div class="col-sm-5">
         <a href="logout" class="btn btn-primary pull-right">Logout</a>
diff --git a/views/token-issuer.php b/views/token-issuer.php
new file mode 100644
index 0000000000000000000000000000000000000000..12ce18ace7b4b42f10d9d7ada6e5c064351b1d58
--- /dev/null
+++ b/views/token-issuer.php
@@ -0,0 +1,58 @@
+<?php
+include 'include/header.php';
+?>
+
+<div class="row">
+    <div class="col-sm-6 col-sm-offset-3">
+        <p>This panel can be used to generate tokens to be used from command line interfaces and desktop applications.</p>
+        <br/>
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                <h3 class="panel-title">Token issuer</h3>
+            </div>
+            <div class="panel-body">
+                <form class="form-horizontal" action="<?php echo $contextRoot . '/token-issuer'; ?>" method="post">
+                    <div class="form-group">
+                        <label for="service" class="col-sm-4 control-label">Service</label>
+                        <div class="col-sm-8">
+                            <select class="form-control" id="service" name="audit">
+                                <?php
+                                foreach ($config->services as $service) {
+                                    echo "<option value=\"$service->id\">$service->label</option>";
+                                }
+                                ?>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <label for="lifespan" class="col-sm-4 control-label">Duration (hours)</label>
+                        <div class="col-sm-8">
+                            <select class="form-control" id="lifespan" name="lifespan">
+                                <?php
+                                foreach ($config->lifespans as $lifespan) {
+                                    echo "<option>$lifespan</option>";
+                                }
+                                ?>
+                            </select>
+                        </div>
+                    </div>
+                    <div class="form-group">
+                        <div class="col-sm-8 col-sm-offset-4">
+                            <input type="submit" class="btn btn-primary" value="Download token" />
+                        </div>
+                    </div>
+                    <input type="hidden" value="<?php echo $csrfToken; ?>" name="csrf_token" />
+                </form>
+            </div>
+        </div>
+        <br/>
+        <p class="text-center">
+            <strong>
+                <a href="<?php echo $contextRoot . '/account'; ?>">Back to account manager</a>
+            </strong>
+        </p>
+    </div>
+</div>
+
+<?php
+include 'include/footer.php';