# HG changeset patch # User Gustavo Andres Morero # Date 1597349938 10800 # Thu Aug 13 17:18:58 2020 -0300 # Node ID 14b8533e0a816cb5cb575411c672c37e8a2d7aaf # Parent ef1f7085afbac7cf1a084fe68260332f42e183a2 adding basic documentation on README.md diff --git a/MANIFEST.in b/MANIFEST.in --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,2 +1,2 @@ -include README.rst +include README.md recursive-include nlotp/templates * diff --git a/README.md b/README.md new file mode 100644 --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# Netlandish One-Time Password (Two-Factor Authentication) + +Netlandish OTP is a customization of [`django-otp`](https://github.com/django-otp/django-otp) app, using TOTP and Static plugins. + +The user verification process is done by first checking a TOTP device match, if not found look for user maching Static device (backup codes). + +To enable one-time password, a TOTP verification code needs to be provided, by scanning the QR code or entering a key on an application like Google Authenticator. +Once enabled, backup codes (Static device) are generated and Django user account password will be required for two actions: generating new backup codes and disabling 2FA. + +## Installation + +1. Add `django_otp`, `django_otp.plugins.otp_totp`, `django_otp.plugins.otp_static` and `nlotp` to `INSTALLED_APPS`. + +1. Add `nlotp.middleware.OTPCheckMiddleware` to `MIDDLEWARE`. It must be installed after `django.contrib.auth.middleware.AuthenticationMiddleware`. + +So it will looks like this: + +```python + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + + "django_otp", + "django_otp.plugins.otp_totp", + "django_otp.plugins.otp_static", + "nlotp", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + + "nlotp.middleware.OTPCheckMiddleware", +] + +``` + +## Settings + +### `NLOTP_VERIFY_URL` +Verify page url, user is redirected to this page after login if has 2FA enabled. Defaults to `nlotp:verify-otp` url. + +### `NLOTP_VERIFY_EXCLUDED_URLS` +List of urls excluded from redirect to `NLOTP_VERIFY_URL` if logged in user has 2FA enabled but is not verified. Defaults to `(NLOTP_VERIFY_URL,)`. You should include your app login and logout urls in this list, for example include in your settings: +```python +NLOTP_VERIFY_EXCLUDED_URLS = ( + reverse_lazy('login'), + reverse_lazy('logout'), + NLOTP_VERIFY_URL, +) +``` + +### `NLOTP_2FA_SETUP_REQUIRED` +If 2FA setup is mandatory. Logged in user is forced to setup 2FA, redirected to `NLOTP_2FA_SETUP_URL`. Defaults to `False`. + +### `NLOTP_2FA_SETUP_URL` +2FA setup page url, where OTP setup is done (enable/disable and code generation). Defaults to `nlotp:two-factor-auth` url. + +### `NLOTP_QR_CODE_URL` +User TOTP device QR code generation url, returns a QR code SVG image. Defaults to `nlotp:qr-code` url. + +### `NLOTP_SETUP_EXCLUDED_URLS` +List of urls excluded from redirect to `NLOTP_2FA_SETUP_URL` when `NLOTP_2FA_SETUP_REQUIRED` is enabled and user has not setup 2FA. Defaults to `(NLOTP_2FA_SETUP_URL, NLOTP_QR_CODE_URL)`. You should include your app login and logout urls in this list, for example include in your settings: +```python +NLOTP_SETUP_EXCLUDED_URLS = ( + reverse_lazy('login'), + reverse_lazy('logout'), + NLOTP_2FA_SETUP_URL, + NLOTP_QR_CODE_URL, +) +``` + +### `OTP_TOTP_ISSUER` +`django-otp` setting, this is how your app will be shown in the auth app (e.g. Google Authenticator). Defaults to `None`. It can be string or a callable. For example: +```python +def totp_issuer(device): + return f'Your app name ({device.user.email})' + +OTP_TOTP_ISSUER = totp_issuer +``` +Refer to [`django-otp` docs](https://django-otp-official.readthedocs.io/en/stable/overview.html#totp-settings) for more details. + +## Templates + +There are 2 basic templates provided with the app, that you should consider to override. + +### `nlotp/verify_otp.html` + +Verify page template. + +#### Context variables + +`form`: customized version of `django-otp` `OTPTokenForm`; `otp_device` and `otp_token` fields should be included the template. + +### `nlotp/two_factor_auth.html` + +2FA setup page template. + +#### Context variables + +`form`: user model form that includes `authentication_code` field (should be included in template if 2FA is disabled for user) and `password` (should be included in template if 2FA is enabled for user). + +`totp_device`: user `TOTPDevice`, it could be `confirmed` (2FA enabled) or not (2FA disabled). + +`static_device`: user `StaticDevice`, it could be `confirmed` (2FA enabled) or not (2FA disabled). + +`show_codes`: if backup codes (static device) should be shown, it's set to `True` after user enables 2FA or requests new backup codes. + +`qr_code_url`: `NLOTP_QR_CODE_URL` url. + +`setup_required`: `NLOTP_2FA_SETUP_REQUIRED` flag. + +## Utils + +### `utils.get_user_totp_device` + +Gets `user` `TOTPDevice` or creates a new one (not `confirmed`). + +### `utils.get_user_static_device` + +Gets `user` `StaticDevice` or creates a new one (not `confirmed`). Adds 10 new backup codes if created. + +### `utils.generate_user_static_tokens` + +Adds `count` (10 by default) backup codes to `user` `StaticDevice`. + +## Mixins + +### `mixins.TOTPDeviceMixin` + +Simple mixin that inserts logged in user TOTP device in view context. diff --git a/README.rst b/README.rst deleted file mode 100644 --- a/README.rst +++ /dev/null @@ -1,4 +0,0 @@ -Netlandish one-time passwords (Two-Factor Authentication) -======================================================== - -Netlandish custom Django OTP app. diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from setuptools import setup project_name = "nlotp" -long_description = open("README.rst").read() +long_description = open("README.md").read() # Idea from django-registration setup.py packages, data_files = [], []