django-otp-webauthn

raw JSON →
0.8.0 verified Mon Apr 27 auth: no python

FIDO2 WebAuthn support for django-otp, enabling passkey authentication. Version 0.8.0 requires Python >=3.10 and Django 4.2+. Active development, monthly releases.

pip install django-otp-webauthn
error django_otp_webauthn.models.WebAuthnDevice.user.RelatedObjectDoesNotExist: WebAuthnDevice has no user.
cause Migrated from django-otp's old 'user' field to generic foreign key; existing data may be missing the content_type_id.
fix
Run manage.py migrate django_otp_webauthn --fake-initial and ensure Django's content types are migrated.
error ImportError: cannot import name 'WebAuthnBackend' from 'django_otp_webauthn.backends'
cause Trying to import from a version <0.5.0 where the backend was named 'WebAuthnBackend' but was later moved to 'WebAuthnAuthenticationBackend'. In 0.5.0+ it's back to 'WebAuthnBackend'.
fix
Use: from django_otp_webauthn.backends import WebAuthnBackend (for 0.5.0+), or downgrade if on older version.
error ValueError: The 'rp_id' must be a valid domain, not a URL.
cause Setting OTP_WEBAUTHN_RP_ID to a URL like 'https://example.com' instead of just the domain 'example.com'.
fix
OTP_WEBAUTHN_RP_ID = 'example.com' (no scheme or path)
breaking Starting from version 0.7.0, the model field 'webauthn_key' was renamed to 'credential_public_key'. This breaks custom code that references the old field name.
fix Update any direct field references: old_obj.webauthn_key -> old_obj.credential_public_key
deprecated The setting OTP_WEBAUTHN_ALLOWED_ORIGINS is deprecated in 0.8.0; use OTP_WEBAUTHN_ORIGIN (single string) instead.
fix Replace OTP_WEBAUTHN_ALLOWED_ORIGINS = ['https://example.com'] with OTP_WEBAUTHN_ORIGIN = 'https://example.com'
gotcha The 'webauthn' library (not django-otp-webauthn) uses a non-standard field name 'credential_id' in its return dict. When accessing credential data, use 'credential_id' (lowercase) not 'credentialID'.
fix Always use .get('credential_id') on the response dict, not .credentialID or .get('credentialID')

Minimal setup: add to INSTALLED_APPS, backend, and configure relying party.

INSTALLED_APPS = [
    ...
    'django_otp',
    'django_otp_webauthn',
]

AUTHENTICATION_BACKENDS = [
    'django.contrib.auth.backends.ModelBackend',
    'django_otp_webauthn.backends.WebAuthnBackend',
]

OTP_WEBAUTHN_RP_NAME = 'My App'
OTP_WEBAUTHN_RP_ID = 'example.com'
OTP_WEBAUTHN_ORIGIN = 'https://example.com'