diff --git a/hiboo/account/login.py b/hiboo/account/login.py
index 316189eb545682dca2bf8e06b88bec27b8e01179..590ade9fc4dae9d21f67bb0b814221cca3906dda 100644
--- a/hiboo/account/login.py
+++ b/hiboo/account/login.py
@@ -2,6 +2,7 @@ from hiboo import models, utils, security
 from hiboo.account import blueprint, forms
 from flask_babel import lazy_gettext as _
 from flask import session
+from authlib.jose import JsonWebToken
 
 import datetime
 import flask_login
@@ -32,6 +33,20 @@ def signout():
 
 @blueprint.route("/signup", methods=["GET", "POST"])
 def signup():
+    if not flask.current_app.config['OPEN_SIGNUP']:
+        token = flask.request.args.get('token') or flask.abort(403)
+        key = flask.current_app.config["SECRET_KEY"]
+        jwt = JsonWebToken(['HS512'])
+        claims_options = {
+            'exp': {'essential': True, 'value': datetime.datetime.now().timestamp()},
+            'aud': {'essential': True, 'value': flask.url_for('.signup')}
+        }
+        try:
+            claims = jwt.decode(token, key, claims_options=claims_options)
+            claims.validate()
+        except Exception as e:
+            flask.flash(_("Invalid or expired signup link"), "danger")
+            return flask.redirect(flask.url_for(".signin"))
     form = forms.SignupForm()
     if form.validate_on_submit():
         conflict = models.User.query.filter_by(username=form.username.data).first()
@@ -72,4 +87,4 @@ def reset(token_uuid):
         models.db.session.commit()
         flask.flash(_("Successfully reset your password"), "success")
         return flask.redirect(flask.url_for(".signin"))
-    return flask.render_template("account_reset.html", form=form)
\ No newline at end of file
+    return flask.render_template("account_reset.html", form=form)
diff --git a/hiboo/configuration.py b/hiboo/configuration.py
index e36dbe777df23e60bcdd034fe57bc4df10f93e3f..a2005a9e3ba66818fc4abea8068d6d15e8436be8 100644
--- a/hiboo/configuration.py
+++ b/hiboo/configuration.py
@@ -14,6 +14,7 @@ DEFAULT_CONFIG = {
     'TEMPLATES_AUTO_RELOAD': False,
     'MAIL_DOMAIN': 'tedomum.net',
     'WEBSITE_NAME': 'Hiboo',
+    'OPEN_SIGNUP': True,
     'API_TOKEN': 'changeMe'
 }
 
diff --git a/hiboo/user/templates/user_list.html b/hiboo/user/templates/user_list.html
index 1414649c8f3933b9eae9fbf2be8f1c0a9ababb95..78ac14af3a96e966cb2004a3cee62d9d0f80b479 100644
--- a/hiboo/user/templates/user_list.html
+++ b/hiboo/user/templates/user_list.html
@@ -24,3 +24,7 @@
   </div>
 </div>
 {% endblock %}
+
+{% block actions %}
+<a href="{{ url_for(".invite") }}" class="btn btn-primary">{% trans %}Sign-up link{% endtrans %}</a>
+{% endblock %}
diff --git a/hiboo/user/views.py b/hiboo/user/views.py
index 7a814435e2a8578f03e7f8f74fe18185a097691c..5ea587490d8442d3f83cb124c20ea31aeb297c44 100644
--- a/hiboo/user/views.py
+++ b/hiboo/user/views.py
@@ -1,6 +1,7 @@
 from hiboo.user import blueprint, forms
 from hiboo import models, utils, security
 from flask_babel import lazy_gettext as _
+from authlib.jose import jwt
 
 import datetime
 import flask
@@ -39,4 +40,21 @@ def password_reset(user_uuid):
     models.db.session.commit()
     reset_link = flask.url_for("account.reset", token_uuid=token.uuid, _external=True)
     flask.flash(_("Reset link: {}").format(reset_link), "success")
-    return flask.redirect(flask.url_for(".details", user_uuid=user.uuid))
\ No newline at end of file
+    return flask.redirect(flask.url_for(".details", user_uuid=user.uuid))
+
+
+@blueprint.route("/invite", methods=["GET", "POST"])
+@security.admin_required()
+@security.confirmation_required("generate a signup link")
+def invite():
+    expired = datetime.datetime.now() + datetime.timedelta(days=1)
+    payload = {
+        "exp": int(expired.timestamp()),
+        "aud": flask.url_for('account.signup')
+    }
+    header = {"alg": "HS512"}
+    key = flask.current_app.config["SECRET_KEY"]
+    token = jwt.encode(header, payload, key)
+    signup_link = flask.url_for("account.signup", token=token, _external=True)
+    flask.flash(_("Signup link: {}").format(signup_link), "success")
+    return flask.redirect(flask.url_for("user.list"))