diff --git a/dictionaries/perun.definition.json b/dictionaries/perun.definition.json
index 48738f7be5562fa6fd9c9a6dc4a26b514355cddd..95c03762a4318817920e5a542bd105760f360e43 100644
--- a/dictionaries/perun.definition.json
+++ b/dictionaries/perun.definition.json
@@ -178,5 +178,17 @@
   "403_is_eligible_default_contact": {
     "en": "If you think you have used an account which meets the criteria, and you are still prevented from logging in to the service, please contact us at",
     "cs": "Pokud si myslíte, že používáte správný účet a přístup je Vám odmítnut neprávem, prosíme kontakujte nás na"
+  },
+  "create_member_exception_text": {
+    "en": "Error occured when activating account. Please try again later.",
+    "cs": "Při aktivování účtu se objevila chyba. Zkuste to prosím za chvíli."
+  },
+  "create_member_success_text": {
+    "en": "Your account was successfully activated. Changes should be noticeable within 5-10 minutes.",
+    "cs": "Váš účet byl úspěšně aktivován. Změny by se měly projevit do 5-10 minut."
+  },
+  "create_member_button": {
+    "en": "Continue",
+    "cs": "PokraÄŤovat"
   }
 }
diff --git a/lib/AdapterRpc.php b/lib/AdapterRpc.php
index 95a2291b685f2e11899e0edaff04d30abe0f2c12..fb07474584b05c27edca0e6670a6f36808269a67 100644
--- a/lib/AdapterRpc.php
+++ b/lib/AdapterRpc.php
@@ -844,6 +844,24 @@ class AdapterRpc extends Adapter
         return $facilitiesWithAttributes;
     }
 
+    public function getExtSourceByName(string $extSourceName)
+    {
+        return $this->connector->get(
+            'extSourcesManager',
+            'getExtSourceByName',
+            ['name' => $extSourceName]
+        );
+    }
+
+    public function createMember(int $voId, int $extSourceId, string $extLogin)
+    {
+        $this->connector->post(
+            'membersManager',
+            'createMember',
+            ['vo' => $voId, 'extSource' => $extSourceId, "login" => $extLogin]
+        );
+    }
+
     private function getAttributes($perunAttrs, $attrNamesMap)
     {
         $attributes = [];
diff --git a/lib/Auth/Process/PerunCreateMember.php b/lib/Auth/Process/PerunCreateMember.php
new file mode 100644
index 0000000000000000000000000000000000000000..e911ce4cd004be7dac9e725816f76895d14bf2d8
--- /dev/null
+++ b/lib/Auth/Process/PerunCreateMember.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace SimpleSAML\Module\perun\Auth\Process;
+
+use SimpleSAML\Auth\State;
+use SimpleSAML\Configuration;
+use SimpleSAML\Auth\ProcessingFilter;
+use SimpleSAML\Error\Exception;
+use SimpleSAML\Module;
+use SimpleSAML\Module\perun\AdapterRpc;
+use SimpleSAML\Module\perun\Exception as PerunException;
+use SimpleSAML\Module\perun\PerunConstants;
+use SimpleSAML\Utils\HTTP;
+
+class PerunCreateMember extends ProcessingFilter
+{
+    public const TEMPLATE_URL = 'perun:perun-create-member-tpl.php';
+
+    public const REDIRECT_URL = 'perun/perun_create_member.php';
+
+    public const CALLBACK_URL = 'perun/perun_create_member_callback.php';
+
+    public const STAGE = 'perun:PerunCreateMember';
+
+    public const PARAM_WAS_EXCEPTION = 'was_exception';
+
+    public const PARAM_STATE_ID = PerunConstants::STATE_ID;
+
+    public const PARAM_CALLBACK = 'callback';
+
+    public const DEBUG_PREFIX = self::STAGE . ' - ';
+
+    public const UID_ATTR = 'uid_attr';
+
+    public const IDP_ENTITY_ID = 'idp_entity_id';
+
+    public const VO_SHORT_NAME = 'vo_short_name';
+
+    private $config;
+
+    private $userIdAttr;
+
+    private $adapter;
+
+    private $idpEntityId;
+
+    private $voShortName;
+
+    public function __construct($config = null, $reserved = null)
+    {
+        if ($config) {
+            parent::__construct($config, $reserved);
+        }
+        $this->config = Configuration::loadFromArray($config);
+        $this->userIdAttr = $this->config->getString(self::UID_ATTR, null);
+        $this->voShortName = $this->config->getString(self::VO_SHORT_NAME, null);
+        $this->idpEntityId = $this->config->getString(self::IDP_ENTITY_ID, null);
+
+        $this->adapter = new AdapterRpc();
+
+        if (empty($this->userIdAttr)) {
+            throw new Exception(
+                self::DEBUG_PREFIX .
+                'Invalid configuration: no attribute configured for extracting UID. Use option \'' . self::UID_ATTR .
+                '\' to configure list of attributes, that should be considered as IDs for a user'
+            );
+        }
+
+        if (empty($this->voShortName)) {
+            throw new Exception(
+                self::DEBUG_PREFIX .
+                'Invalid configuration: no vo_short_name configured. Use option \'' . self::VO_SHORT_NAME .
+                '\' to configure VO in which member should be created.'
+            );
+        }
+
+        if (empty($this->idpEntityId)) {
+            throw new Exception(
+                self::DEBUG_PREFIX .
+                'Invalid configuration: no idp_entity_id configured. Use option \'' . self::IDP_ENTITY_ID .
+                '\' to configure idp for which this filter will be performed.'
+            );
+        }
+    }
+
+    public function process(&$request)
+    {
+
+        if (isset($request[PerunConstants::ATTRIBUTES][$this->userIdAttr[0]])) {
+            $uid = $request[PerunConstants::ATTRIBUTES][$this->userIdAttr[0]];
+        } else {
+            throw new Exception(
+                self::DEBUG_PREFIX . 'missing user id attribute in request.'
+            );
+        }
+
+        $alreadyMember = false;
+        $wasException = false;
+        try {
+            $extSource = $this->adapter->getExtSourceByName($this->idpEntityId);
+            $vo = $this->adapter->getVoByShortName($this->voShortName);
+            $this->adapter->createMember($vo->getId(), $extSource["id"], $uid);
+        } catch (PerunException $e) {
+            if ($e->getName() === 'AlreadyMemberException') {
+                $alreadyMember = true;
+            } else {
+                $wasException = true;
+            }
+        }
+        if ($alreadyMember) {
+            return;
+        }
+
+        $stateId = State::saveState($request, self::STAGE);
+        $url = Module::getModuleURL(self::REDIRECT_URL);
+
+        $params = [];
+        $params[self::PARAM_WAS_EXCEPTION] = $wasException;
+        $params[self::PARAM_CALLBACK] = Module::getModuleURL(self::CALLBACK_URL, [
+            self::PARAM_STATE_ID => $stateId,
+        ]);
+
+        HTTP::redirectTrustedURL($url, $params);
+    }
+}
diff --git a/templates/perun-create-member-tpl.php b/templates/perun-create-member-tpl.php
new file mode 100644
index 0000000000000000000000000000000000000000..958b3cda8affda15c690ae92f2653b897ee3c4f8
--- /dev/null
+++ b/templates/perun-create-member-tpl.php
@@ -0,0 +1,27 @@
+<?php
+
+declare(strict_types=1);
+
+use SimpleSAML\Module\perun\Auth\Process\PerunCreateMember;
+
+$this->includeAtTemplateBase('includes/header.php');
+?>
+
+  <div class="row">
+    <div>
+        <?php if ($this->data[PerunCreateMember::PARAM_WAS_EXCEPTION]) { ?>
+          <p><?php echo $this->t('{perun:perun:create_member_exception_text}'); ?></p>
+        <?php } else { ?>
+          <p><?php echo $this->t('{perun:perun:create_member_success_text}'); ?></p>
+          <a class="btn btn-lg btn-block btn-primary"
+             href="<?php echo $this->data[PerunCreateMember::PARAM_CALLBACK]; ?>">
+              <?php echo $this->t('{perun:perun:create_member_button}'); ?>
+          </a>
+        <?php } ?>
+
+    </div>
+  </div>
+
+<?php
+$this->includeAtTemplateBase('includes/footer.php');
+?>
\ No newline at end of file
diff --git a/www/perun_create_member.php b/www/perun_create_member.php
new file mode 100644
index 0000000000000000000000000000000000000000..1efc1736d1101c20f24f30450cba6b8a6e50f596
--- /dev/null
+++ b/www/perun_create_member.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+use SimpleSAML\Configuration;
+use SimpleSAML\Module\perun\Auth\Process\PerunCreateMember;
+use SimpleSAML\XHTML\Template;
+
+$config = Configuration::getInstance();
+$t = new Template($config, PerunCreateMember::TEMPLATE_URL);
+$t->data[PerunCreateMember::PARAM_CALLBACK] = $_REQUEST[PerunCreateMember::PARAM_CALLBACK];
+$t->data[PerunCreateMember::PARAM_WAS_EXCEPTION] = $_REQUEST[PerunCreateMember::PARAM_WAS_EXCEPTION];
+
+$t->show();
diff --git a/www/perun_create_member_callback.php b/www/perun_create_member_callback.php
new file mode 100644
index 0000000000000000000000000000000000000000..4ba5e340ef26c44c6e897fdb0e4df43b0b28e07f
--- /dev/null
+++ b/www/perun_create_member_callback.php
@@ -0,0 +1,14 @@
+<?php
+
+use SimpleSAML\Auth\ProcessingChain;
+use SimpleSAML\Auth\State;
+use SimpleSAML\Error\BadRequest;
+use SimpleSAML\Module\perun\Auth\Process\PerunCreateMember;
+
+if (empty($_REQUEST[PerunCreateMember::PARAM_STATE_ID])) {
+    throw new BadRequest('Missing required \'' . PerunCreateMember::PARAM_STATE_ID . '\' query parameter.');
+}
+
+$state = State::loadState($_REQUEST[PerunCreateMember::PARAM_STATE_ID], PerunCreateMember::STAGE);
+
+ProcessingChain::resumeProcessing($state);