diff --git a/passbook/recovery/__init__.py b/passbook/recovery/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/recovery/apps.py b/passbook/recovery/apps.py
new file mode 100644
index 000000000..0a6db1a06
--- /dev/null
+++ b/passbook/recovery/apps.py
@@ -0,0 +1,11 @@
+"""passbook Recovery app config"""
+from django.apps import AppConfig
+
+
+class PassbookRecoveryConfig(AppConfig):
+    """passbook Recovery app config"""
+
+    name = 'passbook.recovery'
+    label = 'passbook_recovery'
+    verbose_name = 'passbook Recovery'
+    mountpoint = 'recovery/'
diff --git a/passbook/recovery/management/__init__.py b/passbook/recovery/management/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/recovery/management/commands/__init__.py b/passbook/recovery/management/commands/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/passbook/recovery/management/commands/create_recovery_key.py b/passbook/recovery/management/commands/create_recovery_key.py
new file mode 100644
index 000000000..49a3fc3ef
--- /dev/null
+++ b/passbook/recovery/management/commands/create_recovery_key.py
@@ -0,0 +1,46 @@
+"""passbook recovery createkey command"""
+from datetime import timedelta
+from getpass import getuser
+
+from django.core.management.base import BaseCommand
+from django.urls import reverse
+from django.utils.timezone import now
+from django.utils.translation import gettext as _
+from structlog import get_logger
+
+from passbook.core.models import Nonce, User
+from passbook.lib.config import CONFIG
+
+LOGGER = get_logger()
+
+
+class Command(BaseCommand):
+    """Create Nonce used to recover access"""
+
+    help = _('Create a Key which can be used to restore access to passbook.')
+
+    def add_arguments(self, parser):
+        parser.add_argument('duration', default=1, action='store',
+                            help='How long the token is valid for (in years).')
+        parser.add_argument('user', action='store',
+                            help='Which user the Token gives access to.')
+
+    def get_url(self, nonce: Nonce) -> str:
+        """Get full recovery link"""
+        path = reverse('passbook_recovery:use-nonce', kwargs={'uuid': str(nonce.uuid)})
+        return f"https://{CONFIG.y('domain')}{path}"
+
+    def handle(self, *args, **options):
+        """Create Nonce used to recover access"""
+        duration = int(options.get('duration', 1))
+        delta = timedelta(days=duration * 365.2425)
+        _now = now()
+        expiry = _now + delta
+        user = User.objects.get(username=options.get('user'))
+        nonce = Nonce.objects.create(
+            expires=expiry,
+            user=user,
+            description=f'Recovery Nonce generated by {getuser()} on {_now}')
+        self.stdout.write((f"Store this link safely, as it will allow"
+                           f" anyone to access passbook as {user}."))
+        self.stdout.write(self.get_url(nonce))
diff --git a/passbook/recovery/urls.py b/passbook/recovery/urls.py
new file mode 100644
index 000000000..36a3f93f6
--- /dev/null
+++ b/passbook/recovery/urls.py
@@ -0,0 +1,9 @@
+"""recovery views"""
+
+from django.urls import path
+
+from passbook.recovery.views import UseNonceView
+
+urlpatterns = [
+    path('use-nonce/<uuid:uuid>/', UseNonceView.as_view(), name='use-nonce'),
+]
diff --git a/passbook/recovery/views.py b/passbook/recovery/views.py
new file mode 100644
index 000000000..fd870138e
--- /dev/null
+++ b/passbook/recovery/views.py
@@ -0,0 +1,24 @@
+"""recovery views"""
+from django.contrib import messages
+from django.contrib.auth import login
+from django.http import Http404, HttpRequest, HttpResponse
+from django.shortcuts import get_object_or_404, redirect
+from django.utils.translation import gettext as _
+from django.views import View
+
+from passbook.core.models import Nonce
+
+
+class UseNonceView(View):
+    """Use nonce to login"""
+
+    def get(self, request: HttpRequest, uuid: str) -> HttpResponse:
+        """Check if nonce exists, log user in and delete nonce."""
+        nonce: Nonce = get_object_or_404(Nonce, pk=uuid)
+        if nonce.is_expired:
+            nonce.delete()
+            raise Http404
+        login(request, nonce.user, backend='django.contrib.auth.backends.ModelBackend')
+        nonce.delete()
+        messages.warning(request, _("Used recovery-link to authenticate."))
+        return redirect('passbook_core:overview')
diff --git a/passbook/root/settings.py b/passbook/root/settings.py
index e09dfe6eb..0c7041798 100644
--- a/passbook/root/settings.py
+++ b/passbook/root/settings.py
@@ -72,6 +72,7 @@ INSTALLED_APPS = [
     'passbook.api.apps.PassbookAPIConfig',
     'passbook.lib.apps.PassbookLibConfig',
     'passbook.audit.apps.PassbookAuditConfig',
+    'passbook.recovery.apps.PassbookRecoveryConfig',
 
     'passbook.sources.ldap.apps.PassbookSourceLDAPConfig',
     'passbook.sources.oauth.apps.PassbookSourceOAuthConfig',