Django Custom Model Manager

Slack has this feature “Magic link” which allows the user to login into the slack app without a password. This Passwordless login system is very convenient for end users because the user doesn’t have to remember and type the password. This “Magic Link” method just requires the user to enter their email address and they have to verify their identity by clicking the link sent to the email address provided.

We can implement passwordless login system in django with django-sesame library.

django sesame

django-sesame provides the one-click login for your Django project. It uses specially crafted URLs containing an authentication token, for example: http://example.com/?url_auth_token=XXXXXXXXXX

Here user’s identity is verified using the auth token present in the url.

Setup

Clone this repo and install requirements.

Install requirements

pip install -r requirements.txt

Configure Django project to use django sesame

Steps needs to be done

  1. django sesame authentication middleware
  2. django sesame authentication backend
  3. email smtp configuration (used for sending magic link via email)
# settings.py

# setup default login page
LOGIN_URL = '/customers/login/'

# add auth middleware and auth backend
MIDDLEWARE += ['sesame.middleware.AuthenticationMiddleware']
AUTHENTICATION_BACKENDS += ['sesame.backends.ModelBackend']

# configure with your own smtp server settings
EMAIL_HOST = ""
EMAIL_PORT = 2587
EMAIL_HOST_USER = ""
EMAIL_HOST_PASSWORD = ""
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = "Admin<[email protected]>" 

generate login token

Steps to generate login token, django sesame provides 2 methods that we can use to generate auth token.

1: Using get_query_string Use this method if you just want to build url with auth token.

#generate login token
from django.contrib.auth.models import User
from sesame import utils

email = "[email protected]"
user = User.objects.get(email=email)
login_token = utils.get_query_string(user)
login_link = "http://127.0.0.1:8000/customers/{}".format(login_token)

2: Using get_parameters Use this method if you build url on your own.

#get_parameters
from django.contrib.auth.models import User
from sesame import utils

email = "[email protected]"
user = User.objects.get(email=email)
login_token = utils.get_parameters(user)
login_link = "http://127.0.0.1:8000/customers/?method=magic&url_auth_token={}".format(login_token['url_auth_token'] )

#settings.py
#customize auth token name

SESAME_TOKEN_NAME = "url_auth_token"

#settings.py
# set token expiry

SESAME_MAX_AGE = 6 * 60 * 60 # 6 hour

Send email

Make sure you have configured emails in settings.py

#settings.py
#send email

from django.core.mail import send_mail

html_message = """
<p>Hi there,</p>
<p>Here is your <a href="{}">magic link</a> </p>
<p>Thanks,</p>
<p>Django Admin</p>
""".format(login_link)

send_mail(
    'Django Magic Link',
    html_message,
    '[email protected]',
    [email],
    fail_silently=True,
    html_message = html_message
)

Create a login page

<!--login.html-->
<form class="form" action="{% url 'customers-login' %}" method="POST">
  {% csrf_token %}
  <div class="form-group">
    <input type="email" value="" id="emailId" name="emailId" class="form-control" placeholder="Email Address" required>
  </div>
  <button class="btn btn-danger" type="submit">Send Me Magic Link</button>
</form>

{% if message %}
    <div class="card card-block card-inverse card-success text-xs-center">
        <blockquote class="card-blockquote">
          <p>Magic Link sent to your email address.</p>
        </blockquote>
      </div>
{% endif %}

putting it all together.

#views.py

from django.shortcuts import render
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from sesame import utils
from django.core.mail import send_mail


def login_page(request):
    if request.method == "POST":
        email = request.POST.get("emailId")
        user = User.objects.get(email=email)
        login_token = utils.get_query_string(user)
        login_link = "http://127.0.0.1:8000/customers/{}".format(login_token)

        html_message = """
        <p>Hi there,</p>
        <p>Here is your <a href="{}">magic link</a> </p>
        <p>Thanks,</p>
        <p>Django Admin</p>
        """.format(login_link)

        send_mail(
            'Django Magic Link',
            html_message,
            '[email protected]',
            [email],
            fail_silently=False,
            html_message = html_message
        )
        return render(request, "login.html", context={"message":"Please check your email for magic link."})

    return render(request, "login.html")

@login_required
def customers_home_page(request):
    return render(request, "customers/index.html")

Security Concerns

Must read before you implement this on your project https://github.com/aaugustin/django-sesame#a-few-words-about-security

Note

This tutorial intended for learning and development purpose. Not recommended for production use as security issues involved.