diff --git a/.gitignore b/.gitignore
index abe3ca065ee1df7cca14712e7ced1a639a14e451..bd53f23e55a814e447a4e995561405f262e56583 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,7 @@ __pycache__
 *.pyc
 /static
 env/
+
+.env
+geckodriver.log
+.vscode
\ No newline at end of file
diff --git a/accounts/__init__.py b/accounts/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/accounts/admin.py b/accounts/admin.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c38f3f3dad51e4585f3984282c2a4bec5349c1e
--- /dev/null
+++ b/accounts/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/accounts/apps.py b/accounts/apps.py
new file mode 100644
index 0000000000000000000000000000000000000000..9b3fc5a44939430bfb326ca9a33f80e99b06b5be
--- /dev/null
+++ b/accounts/apps.py
@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class AccountsConfig(AppConfig):
+    name = 'accounts'
diff --git a/accounts/authentication.py b/accounts/authentication.py
new file mode 100644
index 0000000000000000000000000000000000000000..a274bb29ae42bdf4c8951bb5dd37b52abcbb07a5
--- /dev/null
+++ b/accounts/authentication.py
@@ -0,0 +1,23 @@
+import sys
+from accounts.models import ListUser, Token
+
+class PasswordlessAuthenticationBackend(object):
+
+    def authenticate(self, uid):
+        print('uid', uid, file=sys.stderr)
+        if not Token.objects.filter(uid=uid).exists():
+            print('no token found', file=sys.stderr)
+            return None
+        token = Token.objects.get(uid=uid)
+        print('got token', file=sys.stderr)
+        try:
+            user = ListUser.objects.get(email=token.email)
+            print('got user', file=sys.stderr)
+            return user
+        except ListUser.DoesNotExist:
+            print('new user', file=sys.stderr)
+            return ListUser.objects.create(email=token.email)
+
+
+    def get_user(self, email):
+        return ListUser.objects.get(email=email)
\ No newline at end of file
diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py
index 11c400f966ed0c7094c30b8949c95fc23a11a24b..daddd5f632460ced66e4b7796cc785d14c14b6ef 100644
--- a/accounts/migrations/0001_initial.py
+++ b/accounts/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.7 on 2019-11-13 14:32
+# Generated by Django 2.1.7 on 2019-11-13 16:08
 
 from django.db import migrations, models
 
diff --git a/accounts/migrations/__init__.py b/accounts/migrations/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/accounts/models.py b/accounts/models.py
new file mode 100644
index 0000000000000000000000000000000000000000..a80d96b4df2cbedeb5807af34745d682de04d9ed
--- /dev/null
+++ b/accounts/models.py
@@ -0,0 +1,33 @@
+from django.db import models
+from django.contrib.auth.models import (
+    AbstractBaseUser, BaseUserManager, PermissionsMixin
+)
+
+
+class Token(models.Model):
+    email = models.EmailField()
+    uid = models.CharField(max_length=255)
+
+class ListUserManager(BaseUserManager):
+
+    def create_user(self, email):
+        ListUser.objects.create(email=email)
+
+    def create_superuser(self, email, password):
+        self.create_user(email)
+
+class ListUser(AbstractBaseUser, PermissionsMixin):
+    email = models.EmailField(primary_key=True)
+    USERNAME_FIELD = 'email'
+    #REQUIRED_FIELDS = ['email', 'height']
+
+    objects = ListUserManager()
+
+    @property
+    def is_staff(self):
+        return self.email == 'harry.percival@example.com'
+
+    @property
+    def is_active(self):
+        return True
+
diff --git a/accounts/templates/login_email_sent.html b/accounts/templates/login_email_sent.html
new file mode 100644
index 0000000000000000000000000000000000000000..2aa6e4ffd35016363271165018e8bbc5dfa387f9
--- /dev/null
+++ b/accounts/templates/login_email_sent.html
@@ -0,0 +1,7 @@
+<html>
+<h1>Email sent</h1>
+
+<p>Check your email, you'll find a message with a link that will log you into
+the site.</p>
+
+</html>
\ No newline at end of file
diff --git a/accounts/tests.py b/accounts/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..7ce503c2dd97ba78597f6ff6e4393132753573f6
--- /dev/null
+++ b/accounts/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/accounts/tests/test_models.py b/accounts/tests/test_models.py
deleted file mode 100644
index ba08223cf3366fd4e9f4672b1d578015cf7c89c8..0000000000000000000000000000000000000000
--- a/accounts/tests/test_models.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.test import TestCase
-from django.contrib.auth import get_user_model
-
-from accounts.models import Token
-
-User = get_user_model()
-
-
-class UserModelTest(TestCase):
-
-    def test_user_is_valid_with_email_only(self):
-        user = User(email='a@b.com')
-        user.full_clean()  # should not raise
-
-    def test_email_is_primary_key(self):
-        user = User(email='a@b.com')
-        self.assertEqual(user.pk, 'a@b.com')
-
-
-class TokenModelTest(TestCase):
-
-    def test_links_user_with_auto_generated_uid(self):
-        token1 = Token.objects.create(email='a@b.com')
-        token2 = Token.objects.create(email='a@b.com')
-        self.assertNotEqual(token1.uid, token2.uid)
diff --git a/accounts/urls.py b/accounts/urls.py
new file mode 100644
index 0000000000000000000000000000000000000000..10f0e241ced16c3e23ac731cc40a705c2d33ec0d
--- /dev/null
+++ b/accounts/urls.py
@@ -0,0 +1,8 @@
+from django.conf.urls import url
+from accounts import views
+
+urlpatterns = [
+    url(r'^send_email$', views.send_login_email, name='send_login_email'),
+    url(r'^login$', views.login, name='login'),
+    url(r'^logout$', views.logout, name='logout'),
+]
\ No newline at end of file
diff --git a/accounts/views.py b/accounts/views.py
new file mode 100644
index 0000000000000000000000000000000000000000..842a06b518c94c675eb4ab9eba362c69e579b010
--- /dev/null
+++ b/accounts/views.py
@@ -0,0 +1,35 @@
+import uuid
+import sys
+from django.shortcuts import render, redirect
+from django.core.mail import send_mail
+from django.contrib.auth import authenticate
+from django.contrib.auth import login as auth_login, logout as auth_logout
+
+from accounts.models import Token
+
+
+def send_login_email(request):
+    email = request.POST['email']
+    uid = str(uuid.uuid4())
+    Token.objects.create(email=email, uid=uid)
+    print('saving uid', uid, 'for email', email, file=sys.stderr)
+    url = request.build_absolute_uri(f'/accounts/login?uid={uid}')
+    send_mail(
+        'Your login link for Superlists',
+        f'Use this link to log in:\n\n{url}',
+        'noreply@superlists',
+        [email],
+    )
+    return render(request, 'login_email_sent.html')
+
+def login(request):
+    print('login view', file=sys.stderr)
+    uid = request.GET.get('uid')
+    user = authenticate(uid=uid)
+    if user is not None:
+        auth_login(request, user)
+    return redirect('/')
+
+def logout(request):
+    auth_logout(request)
+    return redirect('/')
\ No newline at end of file
diff --git a/functional_tests/test_login.py b/functional_tests/test_login.py
deleted file mode 100644
index efbfe834b9a22530e429b341156a5e16df7cb919..0000000000000000000000000000000000000000
--- a/functional_tests/test_login.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# from django.core import mail
-# from selenium.webdriver.common.keys import Keys
-# import re
-
-# from .base import FunctionalTest
-
-# TEST_EMAIL = 'edith@example.com'
-# SUBJECT = 'Your login link for Superlists'
-
-
-# class LoginTest(FunctionalTest):
-
-#     def test_can_get_email_link_to_log_in(self):
-#         # Edith goes to the awesome superlists site
-#         # and notices a "Log in" section in the navbar for the first time
-#         # It's telling her to enter her email address, so she does
-#         self.browser.get(self.live_server_url)
-#         self.browser.find_element_by_name('email').send_keys(TEST_EMAIL)
-#         self.browser.find_element_by_name('email').send_keys(Keys.ENTER)
-
-#         # A message appears telling her an email has been sent
-#         self.wait_for(lambda: self.assertIn(
-#             'Check your email',
-#             self.browser.find_element_by_tag_name('body').text
-#         ))
-
-#         # She checks her email and finds a message
-#         email = mail.outbox[0]
-#         self.assertIn(TEST_EMAIL, email.to)
-#         self.assertEqual(email.subject, SUBJECT)
-
-#         # It has a url link in it
-#         self.assertIn('Use this link to log in', email.body)
-#         url_search = re.search(r'http://.+/.+$', email.body)
-#         if not url_search:
-#             self.fail(f'Could not find url in email body:\n{email.body}')
-#         url = url_search.group(0)
-#         self.assertIn(self.live_server_url, url)
-
-#         # she clicks it
-#         self.browser.get(url)
-
-#         # she is logged in!
-#         self.wait_for(
-#             lambda: self.browser.find_element_by_link_text('Log out')
-#         )
-#         navbar = self.browser.find_element_by_css_selector('.navbar')
-#         self.assertIn(TEST_EMAIL, navbar.text)
diff --git a/lists/templates/base.html b/lists/templates/base.html
index 7445080ba28b988574fd8360f3e25e8545fd67f8..68208b536f0de780d567e92ecd7dcd2b73e216ee 100644
--- a/lists/templates/base.html
+++ b/lists/templates/base.html
@@ -16,6 +16,18 @@
 
     <div class="container">
 
+      <div class="navbar">
+        {% if user.is_authenticated %}
+          <p>Logged in as {{ user.email }}</p>
+          <p><a id="id_logout" href="{% url 'logout' %}">Log out</a></p>
+        {% else %}
+          <form method="POST" action ="{% url 'send_login_email' %}">
+            Enter email to log in: <input name="email" type="text" />
+            {% csrf_token %}
+          </form>
+        {% endif %}
+      </div>
+
       ​<div class="row">
         ​<div class="col-md-6 col-md-offset-3 jumbotron">
           ​<div class="text-center">
diff --git a/requirements.txt b/requirements.txt
index de63f73237f011b74f8c8aa3b54e3115918a774c..9e64a2a8afc93559ee473f7cbacd2b91667f8b16 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,4 +3,5 @@ Django==2.1.7
 selenium==3.141.0
 gunicorn==19.9.0
 psycopg2
-dj_database_url
\ No newline at end of file
+dj_database_url
+django_mutpy
\ No newline at end of file
diff --git a/superlists/settings.py b/superlists/settings.py
index f7b8385bee49ff5d48d06352fb47ce0243b05c2b..6058613fc9ce259ae66e242d694122575a7127aa 100644
--- a/superlists/settings.py
+++ b/superlists/settings.py
@@ -42,9 +42,16 @@ INSTALLED_APPS = [
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'django_mutpy',
-    'lists'
+    'lists',
+    'accounts',
 ]
 
+AUTH_USER_MODEL = 'accounts.ListUser'
+AUTHENTICATION_BACKENDS = [
+    'accounts.authentication.PasswordlessAuthenticationBackend',
+]
+
+
 MIDDLEWARE = [
     'django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
@@ -138,3 +145,26 @@ STATIC_URL = '/static/'
 # STATICFILES_DIRS = (
 #     os.path.join(BASE_DIR, 'static'),
 # )
+
+EMAIL_HOST = 'smtp.gmail.com'
+EMAIL_HOST_USER = 'empecempc@gmail.com'
+EMAIL_HOST_PASSWORD = os.environ.get('EMAIL_PASSWORD')
+EMAIL_PORT = 587
+EMAIL_USE_TLS = True
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': False,
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+        },
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['console'],
+        },
+    },
+    'root': {'level': 'INFO'},
+}
\ No newline at end of file
diff --git a/superlists/urls.py b/superlists/urls.py
index fc78a74071537b13d312a82edc94c8aefdd5a06e..2b708a7eaf08ae43c72a8b535c50b6d4866291e5 100644
--- a/superlists/urls.py
+++ b/superlists/urls.py
@@ -15,9 +15,12 @@ Including another URLconf
 """
 from django.conf.urls import url, include
 from lists import views, urls as list_urls
+from accounts import urls as accounts_urls
+
 
 urlpatterns = [
     # url(r'^admin/', admin.site.urls),
     url(r'^$', views.home_page, name='home'),
     url(r'^lists/', include(list_urls)),
+    url(r'^accounts/', include(accounts_urls)),
 ]