diff --git a/controllers/issues.php b/controllers/issues.php
index 2f65bc70bff534072acc33aff0fb6ac5367d596a..7c8535a7a74db4526ac5bb02534e02e5b5215845 100644
--- a/controllers/issues.php
+++ b/controllers/issues.php
@@ -1,8 +1,5 @@
 <?php
-/**
- * @property Parsedown $parsedown
- */
-final class IssuesController extends \TracToGitlab\Controller
+final class IssuesController extends TracToGitlab\EventController
 {
     const SECRET_ISSUE_OPEN = 'N]<d5V/6tn/sYNMy';
 
@@ -17,35 +14,36 @@ final class IssuesController extends \TracToGitlab\Controller
             throw new MethodNotAllowedException();
         }
 
-        if ($_SERVER['HTTP_X_GITLAB_TOKEN'] !== self::SECRET_ISSUE_OPEN) {
+        if (!$this->verifySecret(self::SECRET_ISSUE_OPEN)) {
             throw new AccessDeniedException();
         }
 
-        $input = file_get_contents('php://input');
-        $payload = json_decode($input, true);
+        if ($this->getFromPayload('object_attributes', 'action') === 'open') {
+            $username = $this->getFromPayload('user', 'username');
+            $email = $this->getFromPayload('user', 'email');
 
-        if ($payload['object_attributes']['action'] === 'open') {
-            $user = User::findOneByUsername($payload['user']['username'])
-                 ?? User::findOneByEmail($payload['user']['email'])
+            $user = User::findOneByUsername($username)
+                 ?? User::findOneByEmail($email)
                  ?? User::findOneByUsername('gitlab-bot');
 
             if ($user) {
+                $labels = $this->getFromPayload('labels');
                 foreach (self::LABEL_MAPPING as $label => $definition) {
-                    if (!$this->hasLabel($payload, $label)) {
+                    if (!$this->hasLabel($labels, $label)) {
                         continue;
                     }
                     $title = sprintf(
                         $definition[2],
-                        $payload['object_attributes']['iid'],
-                        $payload['object_attributes']['title']
+                        $this->getFromPayload('object_attributes', 'iid'),
+                        $this->getFromPayload('object_attributes', 'title')
                     );
                     $body = Studip\Markup::markAsHtml(
                         $this->parsedown->text(
                             sprintf(
                                 "%s\n\n----\nZum [gitlab-Issue #%u](%s)",
-                                trim($payload['object_attributes']['description']),
-                                $payload['object_attributes']['iid'],
-                                $payload['object_attributes']['url']
+                                $this->getFromPayload('object_attributes', 'description'),
+                                $this->getFromPayload('object_attributes', 'iid'),
+                                $this->getFromPayload('object_attributes', 'url')
                             )
                         )
                     );
@@ -59,18 +57,19 @@ final class IssuesController extends \TracToGitlab\Controller
                     );
 
                     $this->gitlab->issues()->addNote(
-                        $payload['project']['id'],
-                        $payload['object_attributes']['iid'],
+                        $this->getFromPayload('project', 'id'),
+                        $this->getFromPayload('object_attributes', 'iid'),
                         "[Zum Forenbeitrag auf dem Entwicklungsserver]({$url})"
-                    );                }
+                    );
+                }
             }
         }
         $this->render_nothing();
     }
 
-    private function hasLabel(array $change, string $needle): bool
+    private function hasLabel(array $labels, string $needle): bool
     {
-        foreach ($change['labels'] as $label) {
+        foreach ($labels as $label) {
             if ($label['title'] === $needle) {
                 return true;
             }
diff --git a/controllers/users.php b/controllers/users.php
new file mode 100644
index 0000000000000000000000000000000000000000..4e988bee91e2e3d29c69e835bc52317328cca018
--- /dev/null
+++ b/controllers/users.php
@@ -0,0 +1,29 @@
+<?php
+final class UsersController extends TracToGitlab\EventController
+{
+    const SECRET_USER_CREATED = 'q<-%Y7Ys>h(@LHgg';
+
+    public function created_action()
+    {
+        if (!Request::isPost()) {
+            throw new MethodNotAllowedException();
+        }
+
+        if (
+            !$this->verifySecret(self::SECRET_USER_CREATED)
+            || !$this->verifyEventType('System Hook')
+        ) {
+            throw new AccessDeniedException();
+        }
+
+        if ($this->getFromPayload('event_name') === 'user_create') {
+            $this->gitlab->projects()->addMember(
+                $this->gitlabProjectId,
+                $this->getFromPayload('user_id'),
+                10
+            );
+        }
+
+        $this->render_nothing();
+    }
+}
diff --git a/lib/EventController.php b/lib/EventController.php
new file mode 100644
index 0000000000000000000000000000000000000000..bfc6d992973179f4e56d3dc1c336a4e3609202c2
--- /dev/null
+++ b/lib/EventController.php
@@ -0,0 +1,53 @@
+<?php
+namespace TracToGitlab;
+
+abstract class EventController extends Controller
+{
+    private $payload = null;
+
+    protected function verifySecret(string $secret): bool
+    {
+        return $_SERVER['HTTP_X_GITLAB_TOKEN'] === $secret;
+    }
+
+    protected function verifyEventType(string $type): bool
+    {
+        return $_SERVER['HTTP_X_GITLAB_EVENT'] === $type;
+    }
+
+    protected function getFromPayload(...$keys)
+    {
+        if ($this->payload === null) {
+            $this->payload = $this->getPayloadFromRequest() ?? false;
+        }
+        if ($this->payload === false) {
+            throw new \Exception('No payload detected');
+        }
+
+        $result = $this->payload;
+        foreach ($keys as $key) {
+            if (!isset($result[$key])) {
+                throw new \Exception("Invalid payload key {$key}");
+            }
+
+            $result = $result[$key];
+        }
+
+        return $result;
+    }
+
+    private function getPayloadFromRequest(): ?array
+    {
+        $input =file_get_contents('php://input');
+        if (!$input) {
+            return null;
+        }
+
+        $payload = json_decode($input, true);
+        if (!$payload || !is_array($payload)) {
+            return null;
+        }
+
+        return $payload;
+    }
+}
diff --git a/plugin.manifest b/plugin.manifest
index bc90b8c4e25e244b8a1ff10e836f3fb2c172b60e..3fa6aee95693ca11854967d821cd50d1050d62a3 100644
--- a/plugin.manifest
+++ b/plugin.manifest
@@ -1,5 +1,5 @@
 pluginname=Trac to gitlab converter
 pluginclassname=TracToGitlabPlugin
 origin=UOL
-version=1.1
+version=1.1.1
 studipMinVersion=5.0