+
+# Django Mail Auth
+
+[](https://pypi.python.org/pypi/django-mail-auth/)
+[](https://django-mail-auth.readthedocs.io/en/latest/?badge=latest)
+[](https://codecov.io/gh/codingjoe/django-mail-auth)
+[](https://raw.githubusercontent.com/codingjoe/django-mail-auth/main/LICENSE)
+
+Django Mail Auth is a lightweight authentication backend for Django,
+that does not require users to remember passwords.
+
+Django Mail Auth features:
+
+- custom user model support
+- drop in Django admin support
+- drop in Django User replacement
+- drop in Wagtail login replacement
+- extendable SMS support
+
+{alt="screenshot from a login form" width="425px"}
+
+This project was inspired by:
+
+- [Is it time for password-less login?](http://notes.xoxco.com/post/27999787765/is-it-time-for-password-less-login)
+ by [Ben Brown](http://twitter.com/benbrown)
+- [LOGIN WITHOUT PASSWORD MOST SECURE | WAIT.. WHAT?](https://www.lucius.digital/en/blog/login-without-password-most-secure-wait-what)
+ by [Joris Snoek](https://twitter.com/lucius_digital)
+- [django-nopassword](https://github.com/relekang/django-nopassword) by
+ [Rolf Erik Lekang](http://rolflekang.com)
+
+## Installation
+
+Run this command to install `django-mail-auth`:
+
+```
+python3 -m pip install django-mail-auth[wagtail]
+```
+
+## Setup
+
+First add `mailauth` to you installed apps:
+
+```python
+INSTALLED_APPS = [
+ # Django's builtin apps…
+ "mailauth",
+ "mailauth.contrib.admin", # optional
+ "mailauth.contrib.user", # optional
+ # optional, must be included before "wagtail.admin"
+ "mailauth.contrib.wagtail",
+ # other apps…
+]
+```
+
+`mailauth.contrib.admin` is optional and will replace the admin's login
+with token based authentication too.
+
+`mailauth.contrib.user` is optional and provides a new Django User
+model. The new User model needs to be enabled via the `AUTH_USER_MODEL`
+setting:
+
+```python
+# This setting should be either "EmailUser" or
+# any custom subclass of "AbstractEmailUser"
+AUTH_USER_MODEL = "mailauth_user.EmailUser"
+
+# optional, Wagtail only
+WAGTAILUSERS_PASSWORD_ENABLED = False
+```
+
+Next you will need to add the new authentication backend:
+
+```python
+AUTHENTICATION_BACKENDS = (
+ # default, but now optional
+ # This should be removed if you use mailauth.contrib.user or any other
+ # custom user model that does not have a username/password
+ "django.contrib.auth.backends.ModelBackend",
+ # The new access token based authentication backend
+ "mailauth.backends.MailAuthBackend",
+)
+```
+
+Django's `ModelBackend` is only needed, if you still want to support
+password based authentication. If you don't, simply remove it from the
+list.
+
+Last but not least, go to your URL root configuration `urls.py` and add
+the following:
+
+```python
+from django.urls import path
+
+
+urlpatterns = [
+ path("accounts/", include("mailauth.urls")),
+ # optional, must be before "wagtail.admin.urls"
+ path("", include("mailauth.contrib.wagtail.urls")),
+]
+```
+
+That's it!
+
+> [!IMPORTANT]
+> Don't forget to setup you Email backend!
diff --git a/README.rst b/README.rst
deleted file mode 100644
index fba8e4b..0000000
--- a/README.rst
+++ /dev/null
@@ -1,124 +0,0 @@
-================
-Django Mail Auth
-================
-
-|version| |docs| |coverage| |license|
-
-.. figure:: sample.png
- :width: 425
- :alt: screenshot from a login form
-
-Django Mail Auth is a lightweight authentication backend for Django,
-that does not require users to remember passwords.
-
-Django Mail Auth features:
-
-- custom user model support
-- drop in Django admin support
-- drop in Django User replacement
-- drop in Wagtail login replacement
-- extendable SMS support
-
-This project was inspired by:
-
-- `Is it time for password-less login?`_ by `Ben Brown`_
-- `LOGIN WITHOUT PASSWORD MOST SECURE | WAIT.. WHAT?`_ by `Joris Snoek`_
-- `django-nopassword`_ by `Rolf Erik Lekang`_
-
-
-.. _`Rolf Erik Lekang`: http://rolflekang.com
-.. _`django-nopassword`: https://github.com/relekang/django-nopassword
-.. _`Is it time for password-less login?`: http://notes.xoxco.com/post/27999787765/is-it-time-for-password-less-login
-.. _`LOGIN WITHOUT PASSWORD MOST SECURE | WAIT.. WHAT?`: https://www.lucius.digital/en/blog/login-without-password-most-secure-wait-what
-.. _`Ben Brown`: http://twitter.com/benbrown
-.. _`Joris Snoek`: https://twitter.com/lucius_digital
-
-Installation
-------------
-
-Run this command to install ``django-mail-auth``::
-
- python3 -m pip install django-mail-auth[wagtail]
-
-Setup
------
-
-First add ``mailauth`` to you installed apps:
-
-.. code-block:: python
-
- INSTALLED_APPS = [
- # Django's builtin apps…
-
- 'mailauth',
-
- 'mailauth.contrib.admin', # optional
- 'mailauth.contrib.user', # optional
-
- # optional, must be included before "wagtail.admin"
- 'mailauth.contrib.wagtail',
-
-
- # other apps…
- ]
-
-``mailauth.contrib.admin`` is optional and will replace the admin's login
-with token based authentication too.
-
-``mailauth.contrib.user`` is optional and provides a new Django User model.
-The new User model needs to be enabled via the ``AUTH_USER_MODEL`` setting:
-
-.. code-block:: python
-
- # This setting should be either "EmailUser" or
- # any custom subclass of "AbstractEmailUser"
- AUTH_USER_MODEL = 'mailauth_user.EmailUser'
-
- # optional, Wagtail only
- WAGTAILUSERS_PASSWORD_ENABLED = False
-
-
-Next you will need to add the new authentication backend:
-
-.. code-block:: python
-
- AUTHENTICATION_BACKENDS = (
- # default, but now optional
- # This should be removed if you use mailauth.contrib.user or any other
- # custom user model that does not have a username/password
- 'django.contrib.auth.backends.ModelBackend',
-
- # The new access token based authentication backend
- 'mailauth.backends.MailAuthBackend',
- )
-
-Django's ``ModelBackend`` is only needed, if you still want to support
-password based authentication. If you don't, simply remove it from the list.
-
-Last but not least, go to your URL root configuration ``urls.py`` and add the following:
-
-.. code-block:: python
-
- from django.urls import path
-
-
- urlpatterns = [
- path('accounts/', include('mailauth.urls')),
-
- # optional, must be before "wagtail.admin.urls"
- path('', include('mailauth.contrib.wagtail.urls')),
- ]
-
-That's it!
-
-.. note:: Don't forget to setup you Email backend!
-
-.. |version| image:: https://img.shields.io/pypi/v/django-mail-auth.svg
- :target: https://pypi.python.org/pypi/django-mail-auth/
-.. |coverage| image:: https://codecov.io/gh/codingjoe/django-mail-auth/branch/main/graph/badge.svg
- :target: https://codecov.io/gh/codingjoe/django-mail-auth
-.. |license| image:: https://img.shields.io/badge/license-MIT-blue.svg
- :target: :target: https://raw.githubusercontent.com/codingjoe/django-mail-auth/main/LICENSE
-.. |docs| image:: https://readthedocs.org/projects/django-mail-auth/badge/?version=latest
- :target: https://django-mail-auth.readthedocs.io/en/latest/?badge=latest
- :alt: Documentation Status
diff --git a/docs/conf.py b/docs/conf.py
deleted file mode 100644
index d969cc2..0000000
--- a/docs/conf.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""Sphinx configuration file."""
-
-import os
-import sys
-
-import django
-from pkg_resources import get_distribution
-
-os.environ.setdefault("DJANGO_SETTINGS_MODULE", "tests.testapp.settings")
-sys.path.insert(0, os.path.abspath(".."))
-django.setup()
-
-project = "Django Mail Auth"
-copyright = "2022, Johannes Maron"
-release = get_distribution("django-mail-auth").version
-version = ".".join(release.split(".")[:2])
-
-master_doc = "index"
-
-extensions = [
- "sphinx.ext.autodoc",
- "sphinx.ext.napoleon",
- "sphinx.ext.intersphinx",
- "sphinx.ext.doctest",
-]
-
-intersphinx_mapping = {
- "python": ("https://docs.python.org/3", None),
- "django": (
- "https://docs.djangoproject.com/en/stable/",
- "https://docs.djangoproject.com/en/stable/_objects/",
- ),
-}
-
-try:
- import sphinxcontrib.spelling # noqa: F401
-except ImportError:
- pass
-else:
- extensions.append("sphinxcontrib.spelling")
-
- spelling_word_list_filename = "spelling_wordlist.txt"
- spelling_show_suggestions = True
-
-
-autodoc_default_options = {
- "show-inheritance": True,
-}
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 0000000..d286591
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,27 @@
+# Contributing
+
+To run test suite run:
+
+```console
+uv run pytest
+```
+
+To build the documentation run:
+
+```
+uv run mkdocs serve
+```
+
+## The sample app
+
+To run a full example --- e.g. to debug frontend code -- you can run:
+
+```
+uv run tests/manage.py migrate
+uv run tests/manage.py createsuperuser
+# You will be asked for the email address of your new superuser
+uv run tests/manage.py runserver
+```
+
+Next you can go to and log in with your
+newly created superuser.
diff --git a/docs/contributing.rst b/docs/contributing.rst
deleted file mode 100644
index e582053..0000000
--- a/docs/contributing.rst
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../CONTRIBUTING.rst
diff --git a/docs/customizing.md b/docs/customizing.md
new file mode 100644
index 0000000..2bc73e9
--- /dev/null
+++ b/docs/customizing.md
@@ -0,0 +1,117 @@
+# Customizing
+
+## Custom login message (like SMS)
+
+Django Mail Auth can be easily extended. Besides template adaptations it
+is possible to send different messages like SMS. To make those changes,
+you will need to write a custom login form.
+
+### Custom login form
+
+Custom login forms need to inherit from
+[BaseLoginForm][mailauth.forms.BaseLoginForm] and override the
+[save][mailauth.forms.BaseLoginForm.save] method.
+
+The following example is for a login SMS. This will require a custom
+user model with a unique `phone_number` field:
+
+```python
+from django import forms
+from django.contrib.auth import get_user_model
+from django.template import loader
+from mailauth.forms import BaseLoginForm
+
+
+class SmsLoginForm(BaseLoginForm):
+ phone_number = forms.CharField()
+
+ template_name = 'registration/login_sms.txt
+ from_number = None
+
+ def __init__(self, *args, **kwargs):
+ self.twilio_client = TwilioRestClient(
+ settings.TWILIO_SID,
+ settings.TWILIO_AUTH_TOKEN
+ )
+ super().__init__(*args, **kwargs)
+
+ def save(self):
+ phone_number = self.cleaned_data['phone_number']
+ user = get_user_model().objects.get(
+ phone_number=phone_number
+ )
+ context = self.get_context(self.request, user)
+
+ from_number = self.from_number or getattr(
+ settings, 'DEFAULT_FROM_NUMBER'
+ )
+ sms_content = loader.render_to_string(
+ self.template_name, context
+ )
+
+ self.twilio_client.messages.create(
+ to=user.phone_number,
+ from_=from_number,
+ body=sms_content
+ )
+```
+
+To add the new login form, simply add a new login view to your URL
+configuration with the custom form:
+
+```python
+from django.urls import path
+from mailauth.views import LoginView
+
+from .forms import SmsLoginForm
+
+urlpatterns = [
+ path("login/sms/", LoginView.as_view(form_class=SmsLoginForm), name="login-sms"),
+]
+```
+
+### API documentation
+
+:::mailauth.forms.BaseLoginForm
+
+## Custom User Model
+
+For convenience, Django Mail Auth provides a
+[EmailUser][mailauth.contrib.user.models.EmailUser] which is almost identical to Django's built-in
+[User][django.contrib.auth.models.User] but without the
+[password][django.contrib.auth.models.User.password] and
+[username][django.contrib.auth.models.User.username] field. The
+[email][mailauth.contrib.user.models.AbstractEmailUser.email] field serves as a username and is -- different to Django's
+User -- unique and case-insensitive.
+
+### Implementing a custom User model
+
+```python
+from mailauth.contrib.user.models import AbstractEmailUser
+from phonenumber_field.modelfields import PhoneNumberField
+
+
+class SMSUser(AbstractEmailUser):
+ phone_number = phone = PhoneNumberField(
+ _("phone number"), unique=True, db_index=True
+ )
+
+
+class Meta(AbstractEmailUser.Meta):
+ verbose_name = _("user")
+ verbose_name_plural = _("users")
+ swappable = "AUTH_USER_MODEL"
+```
+
+> [!NOTE]
+> Do not forget to adjust your `AUTH_USER_MODEL` to correct `app_label.ModelName`.
+
+### API documentation
+
+:::mailauth.contrib.user.models.AbstractEmailUser
+
+:::mailauth.contrib.user.models.AbstractEmailUser.email
+
+:::mailauth.contrib.user.models.AbstractEmailUser.session_salt
+
+:::mailauth.contrib.user.models.EmailUser
diff --git a/docs/customizing.rst b/docs/customizing.rst
deleted file mode 100644
index bab5776..0000000
--- a/docs/customizing.rst
+++ /dev/null
@@ -1,130 +0,0 @@
-===========
-Customizing
-===========
-
-Custom login message (like SMS)
-_______________________________
-
-Django Mail Auth can be easily extended. Besides template adaptations it is
-possible to send different messages like SMS. To make those changes, you
-will need to write a custom login form.
-
-Custom login form
------------------
-
-Custom login forms need to inherit from :class:`.BaseLoginForm` and override
-the :meth:`save<.BaseLoginForm.save>` method.
-
-The following example is for a login SMS. This will require a
-custom user model with a unique ``phone_number`` field:
-
-.. code-block:: python
-
- from django import forms
- from django.contrib.auth import get_user_model
- from django.template import loader
- from mailauth.forms import BaseLoginForm
-
-
- class SmsLoginForm(BaseLoginForm):
- phone_number = forms.CharField()
-
- template_name = 'registration/login_sms.txt
- from_number = None
-
- def __init__(self, *args, **kwargs):
- self.twilio_client = TwilioRestClient(
- settings.TWILIO_SID,
- settings.TWILIO_AUTH_TOKEN
- )
- super().__init__(*args, **kwargs)
-
- def save(self):
- phone_number = self.cleaned_data['phone_number']
- user = get_user_model().objects.get(
- phone_number=phone_number
- )
- context = self.get_context(self.request, user)
-
- from_number = self.from_number or getattr(
- settings, 'DEFAULT_FROM_NUMBER'
- )
- sms_content = loader.render_to_string(
- self.template_name, context
- )
-
- self.twilio_client.messages.create(
- to=user.phone_number,
- from_=from_number,
- body=sms_content
- )
-
-
-To add the new login form, simply add a new login view to your URL configuration with
-the custom form:
-
-.. code-block:: python
-
- from django.urls import path
- from mailauth.views import LoginView
-
- from .forms import SmsLoginForm
-
- urlpatterns = [
- path(
- 'login/sms/',
- LoginView.as_view(form_class=SmsLoginForm),
- name='login-sms'
- ),
- ]
-
-API documentation
------------------
-
-.. autoclass:: mailauth.forms.BaseLoginForm
- :members:
-
-Custom User Model
-_________________
-
-For convenience, Django Mail Auth provides a
-:class:`EmailUser` which is almost
-identical to Django's built in :class:`User`
-but without the :attr:`password`
-and :attr:`username` field.
-The :attr:`email`
-field serves as a username and is – different to Django's User –
-unique and case insensitive.
-
-Implementing a custom User model
---------------------------------
-
-.. code-block:: python
-
- from mailauth.contrib.user.models import AbstractEmailUser
- from phonenumber_field.modelfields import PhoneNumberField
-
-
- class SMSUser(AbstractEmailUser):
- phone_number = phone = PhoneNumberField(_("phone number"), unique=True, db_index=True)
-
- class Meta(AbstractEmailUser.Meta):
- verbose_name = _("user")
- verbose_name_plural = _("users")
- swappable = "AUTH_USER_MODEL"
-
-.. note:: Do not forget to adjust your ``AUTH_USER_MODEL`` to correct ``app_label.ModelName``.
-
-API documentation
------------------
-
-.. autoclass:: mailauth.contrib.user.models.AbstractEmailUser
- :members:
-
- .. autoattribute:: mailauth.contrib.user.models.AbstractEmailUser.email
- :noindex:
- .. autoattribute:: mailauth.contrib.user.models.AbstractEmailUser.session_salt
- :noindex:
-
-.. autoclass:: mailauth.contrib.user.models.EmailUser
- :members:
diff --git a/docs/index.md b/docs/index.md
new file mode 120000
index 0000000..32d46ee
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1 @@
+../README.md
\ No newline at end of file
diff --git a/docs/index.rst b/docs/index.rst
deleted file mode 100644
index 0a08e0a..0000000
--- a/docs/index.rst
+++ /dev/null
@@ -1,16 +0,0 @@
-.. include:: ../README.rst
-
-All Contents
-============
-
-.. toctree::
- :maxdepth: 2
- :caption: Contents:
- :glob:
-
- usage
- templates
- privacy
- customizing
- settings
- contributing
diff --git a/docs/privacy.md b/docs/privacy.md
new file mode 100644
index 0000000..f363f28
--- /dev/null
+++ b/docs/privacy.md
@@ -0,0 +1,36 @@
+# Privacy
+
+## Anonymization
+
+User privacy is important, not only to meet local regulations, but also
+to protect your users and allow them to exercise their rights. However,
+it's not always practical to delete users, especially if they have
+dependent objects, that are relevant for statistical analysis.
+
+Anonymization is a process of removing the user's personal data whilst
+keeping related data intact. This is done by using the `anomymize`
+method.
+
+::: mailauth.contrib.user.models.AbstractEmailUser.anonymize
+
+This method may be overwritten to provide anonymization for you custom
+user model.
+
+Related objects may also listen to the anonymize signal.
+
+::: mailauth.contrib.user.signals.anonymize
+
+All those methods can be conveniently triggered via the `anonymize`
+admin action.
+
+::: mailauth.contrib.user.admin.AnonymizableAdminMixin
+
+## Liability Waiver
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/docs/privacy.rst b/docs/privacy.rst
deleted file mode 100644
index e180d4b..0000000
--- a/docs/privacy.rst
+++ /dev/null
@@ -1,40 +0,0 @@
-Privacy
-========
-
-Anonymization
--------------
-
-User privacy is important, not only to meet local regulations, but also to
-protect your users and allow them to exercise their rights. However,
-it's not always practical to delete users, especially if they have dependent
-objects, that are relevant for statistical analysis.
-
-Anonymization is a process of removing the user's personal data whilst keeping
-related data intact. This is done by using the ``anomymize`` method.
-
-
-
-.. automethod:: mailauth.contrib.user.models.AbstractEmailUser.anonymize
- :noindex:
-
-This method may be overwritten to provide anonymization for you custom user model.
-
-Related objects may also listen to the anonymize signal.
-
-.. autoclass:: mailauth.contrib.user.signals.anonymize
-
-All those methods can be conveniently triggered via the ``anonymize`` admin action.
-
-.. autoclass:: mailauth.contrib.user.admin.AnonymizableAdminMixin
- :members:
-
-Liability Waiver
-----------------
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/docs/settings.md b/docs/settings.md
new file mode 100644
index 0000000..3023430
--- /dev/null
+++ b/docs/settings.md
@@ -0,0 +1,33 @@
+# Settings
+
+## Mail Auth settings
+
+`LOGIN_URL_TIMEOUT` Default: `900`
+
+Defines how long a login code is valid in seconds.
+
+`LOGIN_REQUESTED_URL` Default: `accounts/login/success`
+
+Defines the URL the user will be redirected to, after requesting an
+authentication message.
+
+`LOGIN_TOKEN_SINGLE_USE` Default: `True`
+
+Defines if a token can be used more than once. If `True`, the same token
+can only be used once and will be invalid the next try. If `False`, the
+same token can be used multiple times and remains valid until expired.
+
+## Django related settings
+
+`DEFAULT_FROM_EMAIL` Default: `'root@example.com'`
+
+The sender email address for authentication emails send by Django Mail
+Auth.
+
+`SECRET_KEY`
+
+> [!WARNING]
+> *Keep it secret, keep it safe!*
+>
+> This key is the foundation of all of Django security measures and for
+> this package.
diff --git a/docs/settings.rst b/docs/settings.rst
deleted file mode 100644
index 670be58..0000000
--- a/docs/settings.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-Settings
-========
-
-Mail Auth settings
-------------------
-
-.. attribute:: LOGIN_URL_TIMEOUT
-
- Default: ``900``
-
- Defines how long a login code is valid in seconds.
-
-.. attribute:: LOGIN_REQUESTED_URL
-
- Default: ``accounts/login/success``
-
- Defines the URL the user will be redirected to, after requesting an
- authentication message.
-
-.. attribute:: LOGIN_TOKEN_SINGLE_USE
-
- Default: ``True``
-
- Defines if a token can be used more than once.
- If ``True``, the same token can only be used once and will be invalid the next try.
- If ``False``, the same token can be used multiple times and remains valid until expired.
-
-Django related settings
------------------------
-
-.. attribute:: DEFAULT_FROM_EMAIL
-
- Default: ``'root@example.com'``
-
- The sender email address for authentication emails send by Django Mail Auth.
-
-.. attribute:: SECRET_KEY
-
- .. attention:: *Keep it secret, keep it safe!*
-
- This key is the foundation of all of Django security measures and for
- this package.
diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt
deleted file mode 100644
index 7cc653a..0000000
--- a/docs/spelling_wordlist.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-admin
-anonymize
-anonymized
-anonymization
-Auth
-boolean
-backend
-Django
-frontend
-mixin
-mixins
-namespace
-subclasses
-tuple
-tuples
-triple
-URL
diff --git a/docs/templates.md b/docs/templates.md
new file mode 100644
index 0000000..6c9687d
--- /dev/null
+++ b/docs/templates.md
@@ -0,0 +1,55 @@
+# Templates
+
+There are a couple relevant templates, that can be overridden to your
+needs.
+
+## Mail Auth templates
+
+### Login templates
+
+`registration/login_requested.html`
+
+This template will be displayed after a user successfully requested a
+login URL. This template is not proved by the package and needs to be
+created.
+
+### Email templates
+
+`registration/login_subject.txt`
+
+This template defines the subject line of the email that will be sent to
+the user.
+
+This template is provided by the package and can be overridden.
+
+`registration/login_email.txt`
+
+This is the plain text template for the email containing the
+authentication URL that will be sent to the user.
+
+This template is provided by the package and can be overridden.
+
+`registration/login_email.html`
+
+This is the HTML template for the email containing the authentication
+URL that will be sent to the user.
+
+This template is optional. If not provided, only plain text emails will
+be sent.
+
+## Django related templates
+
+Mail Auth uses Django's default templates for the login views.
+
+### Login templates
+
+`registration/login.html`
+
+This template displays login form, where a user can request a login URL.
+This template is not proved Django or by the package and needs to be
+created.
+
+`registration/logged_out.html`
+
+This template will be displayed after a successful logout. This template
+is not proved Django or by the package and needs to be created.
diff --git a/docs/templates.rst b/docs/templates.rst
deleted file mode 100644
index 5554a2b..0000000
--- a/docs/templates.rst
+++ /dev/null
@@ -1,61 +0,0 @@
-=========
-Templates
-=========
-
-There are a couple relevant templates, that can be overridden to your needs.
-
-Mail Auth templates
--------------------
-
-Login templates
-~~~~~~~~~~~~~~~
-
-.. attribute:: registration/login_requested.html
-
-This template will be displayed after a user successfully requested a login
-URL. This template is not proved by the package and needs to be created.
-
-Email templates
-~~~~~~~~~~~~~~~
-
-.. attribute:: registration/login_subject.txt
-
-This template defines the subject line of the email that will be sent to
-the user.
-
-This template is provided by the package and can be overridden.
-
-.. attribute:: registration/login_email.txt
-
-This is the plain text template for the email containing the authentication
-URL that will be sent to the user.
-
-This template is provided by the package and can be overridden.
-
-.. attribute:: registration/login_email.html
-
-This is the HTML template for the email containing the authentication URL that
-will be sent to the user.
-
-
-This template is optional. If not provided, only plain text emails will be
-sent.
-
-
-Django related templates
-------------------------
-
-Mail Auth uses Django's default templates for the login views.
-
-Login templates
-~~~~~~~~~~~~~~~
-
-.. attribute:: registration/login.html
-
-This template displays login form, where a user can request a login URL. This
-template is not proved Django or by the package and needs to be created.
-
-.. attribute:: registration/logged_out.html
-
-This template will be displayed after a successful logout. This template is
-not proved Django or by the package and needs to be created.
diff --git a/docs/usage.md b/docs/usage.md
new file mode 100644
index 0000000..e69de29
diff --git a/docs/usage.rst b/docs/usage.rst
deleted file mode 100644
index 72a3355..0000000
--- a/docs/usage.rst
+++ /dev/null
@@ -1 +0,0 @@
-.. include:: ../README.rst
diff --git a/images/logo-dark.svg b/images/logo-dark.svg
new file mode 100644
index 0000000..00946bd
--- /dev/null
+++ b/images/logo-dark.svg
@@ -0,0 +1,13 @@
+
diff --git a/images/logo-light.svg b/images/logo-light.svg
new file mode 100644
index 0000000..d34009d
--- /dev/null
+++ b/images/logo-light.svg
@@ -0,0 +1,13 @@
+
diff --git a/mailauth/contrib/__init__.py b/mailauth/contrib/__init__.py
index e69de29..c511d76 100644
--- a/mailauth/contrib/__init__.py
+++ b/mailauth/contrib/__init__.py
@@ -0,0 +1 @@
+"""Secure login links; no passwords required."""
diff --git a/mailauth/forms.py b/mailauth/forms.py
index 7f3198c..6eeb617 100644
--- a/mailauth/forms.py
+++ b/mailauth/forms.py
@@ -1,5 +1,9 @@
+from __future__ import annotations
+
+import typing
import urllib
+import django
from django import forms
from django.contrib.auth import get_user_model
from django.contrib.sites.shortcuts import get_current_site
@@ -14,17 +18,19 @@
class BaseLoginForm(forms.Form):
next = forms.CharField(widget=forms.HiddenInput, required=False)
- def get_login_url(self, request, token, next=None):
+ def get_login_url(
+ self, request: django.http.request.HttpRequest, token: str, next: str = None
+ ) -> str:
"""
Return user login URL including the access token.
Args:
- request (django.http.request.HttpRequest): Current request.
- token (str): The user specific authentication token.
- next (str): The path the user should be forwarded to after login.
+ request: Current request.
+ token: The user specific authentication token.
+ next: The path the user should be forwarded to after login.
Returns:
- str: User login URL including the access token.
+ User login URL including the access token.
"""
protocol = "https" if request.is_secure() else "http"
@@ -38,26 +44,29 @@ def get_login_url(self, request, token, next=None):
url += f"?next={urllib.parse.quote(next)}"
return url
- def get_token(self, user):
+ def get_token(self, user: django.contrib.auth.base_user.AbstractBaseUser) -> str:
"""Return the access token."""
return MailAuthBackend.get_token(user=user)
- def get_mail_context(self, request, user):
+ def get_mail_context(
+ self,
+ request: django.http.request.HttpRequest,
+ user: django.contrib.auth.base_user.AbstractBaseUser,
+ ) -> dict[str, typing.Any]:
"""
Return the context for a message template render.
Args:
- request (django.http.request.HttpRequest): Current request.
+ request: Current request.
user: The user requesting a login message.
Returns:
- dict:
- A context dictionary including:
- - ``site``
- - ``site_name``
- - ``token``
- - ``login_url``
- - ``user``
+ A context dictionary including:
+ - ``site``
+ - ``site_name``
+ - ``token``
+ - ``login_url``
+ - ``user``
"""
token = self.get_token(user)
diff --git a/mkdocs.yml b/mkdocs.yml
new file mode 100644
index 0000000..692582a
--- /dev/null
+++ b/mkdocs.yml
@@ -0,0 +1,24 @@
+site_name: Django MailAuth
+site_url: https://django-mailauth.codingjoe.dev
+repo_url: https://github.com/codingjoe/django-mail-auth
+plugins:
+ - autorefs
+ - search
+ - mkdocstrings:
+ default_handler: python
+ handlers:
+ python:
+ inventories:
+ - https://docs.python.org/3/objects.inv
+ - https://docs.djangoproject.com/en/stable/objects.inv
+theme:
+ name: material
+ features:
+ - search.suggest
+ - search.share
+ - content.code.select
+markdown_extensions:
+ - github-callouts
+ - pymdownx.highlight:
+ use_pygments: true
+ - pymdownx.superfences
diff --git a/pyproject.toml b/pyproject.toml
index cecb461..a5d5bba 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -7,7 +7,7 @@ name = "django-mail-auth"
authors = [
{ name = "Johannes Maron", email = "johannes@maron.family" },
]
-readme = "README.rst"
+readme = "README.md"
license = { file = "LICENSE" }
keywords = [
"django",
@@ -54,7 +54,11 @@ test = [
"pytest-django",
]
docs = [
- "sphinx",
+ "mkdocs",
+ "mkdocstrings[python]>=0.18",
+ "mkdocs-material",
+ "markdown-callouts",
+ "Pygments",
]
wagtail = [
"wagtail>=6.3",