├── src └── otp_yubikey │ ├── __init__.py │ ├── migrations │ ├── __init__.py │ ├── 0002_auto_20200723_1650.py │ └── 0001_initial.py │ ├── apps.py │ ├── admin.py │ ├── tests.py │ └── models.py ├── test └── test_project │ ├── __init__.py │ ├── templates │ └── registration │ │ └── logged_out.html │ ├── urls.py │ └── settings.py ├── .coveragerc ├── docs ├── source │ ├── changes.rst │ ├── .spell.utf-8.add │ ├── index.rst │ └── conf.py ├── ext │ └── otpdocs.py └── Makefile ├── .gitignore ├── .flake8 ├── .readthedocs.yaml ├── .hgignore ├── .bumpversion.cfg ├── .github └── workflows │ └── test.yaml ├── LICENSE ├── README.rst ├── pyproject.toml └── CHANGES.rst /src/otp_yubikey/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/test_project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/otp_yubikey/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | source = otp_yubikey -------------------------------------------------------------------------------- /test/test_project/templates/registration/logged_out.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/source/changes.rst: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | .. include:: ../../CHANGES.rst 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Python 2 | __pycache__/ 3 | 4 | # setuptools 5 | /MANIFEST 6 | /build/ 7 | /dist/ 8 | /docs/build/ 9 | *.egg-info/ 10 | 11 | # Other tools 12 | /.coverage 13 | /.tox/ 14 | -------------------------------------------------------------------------------- /src/otp_yubikey/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DefaultConfig(AppConfig): 5 | name = 'otp_yubikey' 6 | default_auto_field = 'django.db.models.AutoField' 7 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | # line break before binary operator 4 | W503 5 | # line break after binary operator 6 | W504 7 | # line too long 8 | E501 9 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | build: 4 | os: ubuntu-22.04 5 | tools: 6 | python: '3.12' 7 | 8 | sphinx: 9 | configuration: docs/source/conf.py 10 | 11 | python: 12 | install: 13 | - method: pip 14 | path: . 15 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | # Don't remove this file. 2 | # 3 | # When hatch builds an sdist, it includes any .gitignore and .hgignore it can 4 | # find here or in parent directories. To avoid information leakage, this file 5 | # masks any .hgignore one might have in their home directory. 6 | -------------------------------------------------------------------------------- /docs/ext/otpdocs.py: -------------------------------------------------------------------------------- 1 | """ 2 | Extra stuff for the django-otp Sphinx docs. 3 | """ 4 | 5 | def setup(app): 6 | app.add_crossref_type( 7 | directivename = "setting", 8 | rolename = "setting", 9 | indextemplate = "pair: %s; setting", 10 | ) 11 | -------------------------------------------------------------------------------- /test/test_project/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | import django.contrib.auth.views 3 | from django.urls import path 4 | 5 | 6 | urlpatterns = [ 7 | path('login/', django.contrib.auth.views.LoginView.as_view()), 8 | path('logout/', django.contrib.auth.views.LogoutView.as_view()), 9 | path('admin/', admin.site.urls), 10 | ] 11 | -------------------------------------------------------------------------------- /docs/source/.spell.utf-8.add: -------------------------------------------------------------------------------- 1 | django 2 | otp 3 | yubikey 4 | README 5 | #emoteYubikeDevice 6 | YubikeyDevice 7 | RemoteYubikeyDevice 8 | API 9 | Yubico 10 | Yubico's 11 | APIs 12 | SL 13 | google 14 | php 15 | wiki 16 | ValidationProtocolV20 17 | p 18 | urllib2 19 | HTTPS 20 | urls 21 | #emoteYubiKeyDevice 22 | ValidationService 23 | YubiKeys 24 | admin 25 | YubikeyDeviceAdmin 26 | RemoteYubikeyDeviceAdmin 27 | ValidationServiceAdmin 28 | contrib 29 | ModelAdmin 30 | -------------------------------------------------------------------------------- /.bumpversion.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.1.0 3 | commit = true 4 | message = Version {new_version} 5 | tag = true 6 | 7 | [bumpversion:file:CHANGES.rst] 8 | search = Unreleased 9 | replace = v{new_version} - {now:%B %d, %Y} 10 | 11 | [bumpversion:file:pyproject.toml] 12 | search = version = "{current_version}" 13 | replace = version = "{new_version}" 14 | 15 | [bumpversion:file:docs/source/conf.py] 16 | search = release = '{current_version}' 17 | replace = release = '{new_version}' 18 | -------------------------------------------------------------------------------- /src/otp_yubikey/migrations/0002_auto_20200723_1650.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.14 on 2020-07-23 16:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('otp_yubikey', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='validationservice', 15 | name='use_ssl', 16 | field=models.BooleanField(default=True, help_text='Use HTTPS API URLs by default?', verbose_name='Use SSL'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | test: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - uses: actions/setup-python@v4 14 | with: 15 | python-version: | 16 | 3.9 17 | 3.11 18 | 3.13 19 | 20 | - name: Install hatch 21 | run: pipx install hatch 22 | 23 | - name: Run tests 24 | run: hatch run test:run 25 | 26 | - name: Build docs 27 | run: hatch run docs:make 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4 | software, either in source code form or as a compiled binary, for any purpose, 5 | commercial or non-commercial, and by any means. 6 | 7 | In jurisdictions that recognize copyright laws, the author or authors of this 8 | software dedicate any and all copyright interest in the software to the public 9 | domain. We make this dedication for the benefit of the public at large and to 10 | the detriment of our heirs and successors. We intend this dedication to be an 11 | overt act of relinquishment in perpetuity of all present and future rights to 12 | this software under copyright law. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 18 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 19 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | 21 | For more information, please refer to 22 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | django-otp-yubikey 2 | ================== 3 | 4 | .. include:: ../../README.rst 5 | :end-before: .. end-of-doc-intro 6 | 7 | 8 | Installation 9 | ------------ 10 | 11 | Add otp_yubikey to INSTALLED_APPS after django_otp core:: 12 | 13 | INSTALLED_APPS = [ 14 | ... 15 | 'django_otp', 16 | 'django_otp.plugins.otp_totp', 17 | 'django_otp.plugins.otp_hotp', 18 | 'django_otp.plugins.otp_static', 19 | 20 | 'otp_yubikey', 21 | ] 22 | 23 | 24 | Local Verification 25 | ------------------ 26 | 27 | .. autoclass:: otp_yubikey.models.YubikeyDevice 28 | :members: 29 | 30 | 31 | Remote Verification 32 | ------------------- 33 | 34 | .. autoclass:: otp_yubikey.models.ValidationService 35 | :members: 36 | 37 | .. autoclass:: otp_yubikey.models.RemoteYubikeyDevice 38 | :members: 39 | 40 | 41 | Admin 42 | ----- 43 | 44 | The following :class:`~django.contrib.admin.ModelAdmin` subclasses are 45 | registered with the default admin site. We recommend their use with custom admin 46 | sites as well: 47 | 48 | .. autoclass:: otp_yubikey.admin.YubikeyDeviceAdmin 49 | 50 | .. autoclass:: otp_yubikey.admin.ValidationServiceAdmin 51 | 52 | .. autoclass:: otp_yubikey.admin.RemoteYubikeyDeviceAdmin 53 | 54 | 55 | Changes 56 | ------- 57 | 58 | :doc:`changes` 59 | 60 | 61 | License 62 | ======= 63 | 64 | .. include:: ../../LICENSE 65 | -------------------------------------------------------------------------------- /test/test_project/settings.py: -------------------------------------------------------------------------------- 1 | # django-otp-yubikey test project 2 | 3 | from os.path import abspath, dirname, join 4 | 5 | 6 | def project_path(path): 7 | return abspath(join(dirname(__file__), path)) 8 | 9 | 10 | DEBUG = True 11 | 12 | DATABASES = { 13 | 'default': { 14 | 'ENGINE': 'django.db.backends.sqlite3', 15 | 'NAME': ':memory:', 16 | } 17 | } 18 | 19 | DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' 20 | 21 | INSTALLED_APPS = [ 22 | 'django.contrib.admin', 23 | 'django.contrib.auth', 24 | 'django.contrib.contenttypes', 25 | 'django.contrib.sessions', 26 | 'django.contrib.messages', 27 | 28 | 'django_otp', 29 | 'otp_yubikey', 30 | ] 31 | 32 | MIDDLEWARE = [ 33 | 'django.middleware.security.SecurityMiddleware', 34 | 'django.contrib.sessions.middleware.SessionMiddleware', 35 | 'django.middleware.common.CommonMiddleware', 36 | 'django.middleware.csrf.CsrfViewMiddleware', 37 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 38 | 'django_agent_trust.middleware.AgentMiddleware', 39 | 'django_otp.middleware.OTPMiddleware', 40 | 'django.contrib.messages.middleware.MessageMiddleware', 41 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 42 | ] 43 | 44 | TEMPLATES = [ 45 | { 46 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 47 | 'APP_DIRS': True, 48 | 'DIRS': [ 49 | project_path('templates'), 50 | ], 51 | 'OPTIONS': { 52 | 'context_processors': [ 53 | 'django.template.context_processors.debug', 54 | 'django.template.context_processors.request', 55 | 'django.contrib.auth.context_processors.auth', 56 | 'django.contrib.messages.context_processors.messages', 57 | ], 58 | }, 59 | }, 60 | ] 61 | 62 | SECRET_KEY = 'PWuluw4x48GkT7JDPzlDQsBJC8pjIIiqodW9MuMYcU315YEkGJL41i5qooJsg3Tt' 63 | 64 | ROOT_URLCONF = 'test_project.urls' 65 | 66 | USE_TZ = True 67 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/django-otp-yubikey?color=blue 2 | :target: https://pypi.org/project/django-otp-yubikey/ 3 | :alt: PyPI 4 | .. image:: https://img.shields.io/readthedocs/django-otp-yubikey 5 | :target: https://django-otp-yubikey.readthedocs.io/ 6 | :alt: Documentation 7 | .. image:: https://img.shields.io/badge/github-django--agent--trust-green 8 | :target: https://github.com/django-otp/django-otp-yubikey 9 | :alt: Source 10 | 11 | This is a django-otp plugin that handles `YubiKey 12 | `_ devices using the Yubico OTP algorithm. This 13 | includes two device definitions: one to verify YubiKey tokens locally and 14 | another to verify them against a `web service 15 | `_. 16 | 17 | See `django-otp `_ for more information on 18 | the OTP framework. 19 | 20 | .. end-of-doc-intro 21 | 22 | 23 | Development 24 | ----------- 25 | 26 | This project is built and managed with `hatch`_. If you don't have hatch, I 27 | recommend installing it with `pipx`_: ``pipx install hatch``. 28 | 29 | ``pyproject.toml`` defines several useful scripts for development and testing. 30 | The default environment includes all dev and test dependencies for quickly 31 | running tests. The ``test`` environment defines the test matrix for running the 32 | full validation suite. Everything is executed in the context of the Django 33 | project in test/test\_project. 34 | 35 | As a quick primer, hatch scripts can be run with ``hatch run [:]