
Security News
vlt Launches "reproduce": A New Tool Challenging the Limits of Package Provenance
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
FIDO2 WebAuthn support for django-otp: lets your users authenticate with Passkeys
This package provides an implementation of WebAuthn Passkeys for Django. It is written as a plugin for the Django OTP framework for multi-factor authentication. Under the hood, this package uses py_webauth to handle all cryptographic operations.
[!IMPORTANT] This package is in development not yet thoroughly tested and documented. The API is subject to change. If you are interested in using this package, please star this repository to show your interest. This will help me prioritize development. If you are interested in contributing, please see the DEVELOPMENT.md file.
Passkeys are supported in most modern browsers. Here is a list of browsers that support Passkeys:
For a complete list, see caniuse.com/webauthn.
To quickly start using Passkeys in your Django project, follow these steps:
Install the package from PyPI:
pip install django-otp-webauthn
Add django_otp_webauthn
to your INSTALLED_APPS
in your Django settings:
INSTALLED_APPS = [
...
"django_otp_webauthn",
...
]
Add the required URLs to your Django project:
# urls.py
from django.urls import include, path
urlpatterns = [
...
path("webauthn/", include("django_otp_webauthn.urls", namespace="otp_webauthn")),
...
]
Add required settings to your Django settings. This example assumes you want to configure for localhost
. You will need to adjust the settings for your production environment.
# settings.py
# The name of the relying party (RP). This is sometimes shown to the user when they register a Passkey.
OTP_WEBAUTHN_RP_NAME = "My Website Inc."
# This is necessary to bind the Passkey to a specific domain. This should be the domain of your website.
OTP_WEBAUTHN_RP_ID = "your-domain.com"
# This is used to check the origin of the request and is used for security. It is similar to Django's CSRF_TRUSTED_ORIGINS setting.
# The origins must always be a subdomain of the RP ID or the RP ID itself.
OTP_WEBAUTHN_ALLOWED_ORIGINS = ["https://your-domain.com", "https://subdomain.your-domain.com"]
Add django_otp_webauthn.backends.WebAuthnBackend
to AUTHENTICATION_BACKENDS
in your Django settings. This step is required to make 'passwordless authentication' work.
If you are exclusively using Passkeys as a secondary verification step, you don't have to add this backend.
```python
AUTHENTICATION_BACKENDS = [
...
"django_otp_webauthn.backends.WebAuthnBackend",
...
]
```
6. Add the registration code to your logged-in user template.
<!-- logged_in_template.html -->
{% load otp_webauthn %}
{% comment %}
This template is displayed when WebAuthn registration is supported.
The template must contain a button with the id `passkey-register-button`. To display status and error messages, include an element with the id `passkey-register-status-message`.
{% endcomment %}
<template id="passkey-registration-available-template">
<div>
<button type="button" id="passkey-register-button">Register Passkey</button>
<div id="passkey-register-status-message"></div>
</div>
</template>
{% comment %}
This template is displayed when WebAuthn registration is not supported.
{% endcomment %}
<template id="passkey-registration-unavailable-template">
<p>Sorry, your browser has no Passkey support</p>
</template>
{% comment %}
This placeholder element will be replaced with either the contents of the `passkey-registration-available-template` or the `passkey-registration-unavailable-template` template.
{% endcomment %}
<span id="passkey-registration-placeholder"></span>
{% comment %}
This template tag renders all the necessary <script> tags for the default registration implementation
{% endcomment %}
{% render_otp_webauthn_register_scripts %}
On your login page, include the following to enable passwordless login:
{% load otp_webauthn %}
<form method="post">
{# Suppose there is an username field on your page that has CSS selector: input[name="username"] #}
<label for="id_username">Username</label>
<input id="id_username" type="text" name="username" autocomplete="username">
{# Other fields omitted for brevity #}
{# This placeholder element will be replaced with either the contents of the `passkey-verification-available-template` or the `passkey-verification-unavailable-template` template. #}
<span id="passkey-verification-placeholder"></span>
{% comment %}
This template is displayed when WebAuthn authentication is supported. Typically, you would want to display a button that the user can click to authenticate using a Passkey.
The template must contain a button with the id `passkey-verification-button`. To display status and error messages, include an element with the id `passkey-verification-status-message`.
{% endcomment %}
<template id="passkey-verification-available-template">
<button type="button" id="passkey-verification-button">Login using a Passkey</button>
<div id="passkey-verification-status-message"></div>
</template>
{% comment %}
This template is displayed when WebAuthn is not supported.
{% endcomment %}
<template id="passkey-verification-unavailable-template">
<p>Sorry, your browser has no Passkey support</p>
</template>
{% comment %}
This template tag renders all the necessary <script> tags for the default verification implementation
To make browsers automatically suggest a Passkey when you focus the username
field, make sure `username_field_selector` is a valid CSS selector.
The username_field_selector parameter is only required to make 'passwordless authentication' work.
{% endcomment %}
{% render_otp_webauthn_auth_scripts username_field_selector="input[name='username']" %}
</form>
Don't forget to run migrations:
python manage.py migrate
That's it! You should now see a "Register Passkey" button on your logged-in user template. Clicking this button will start the registration process. After registration, you should see a "Login using a Passkey" button on your login page. Clicking this button will prompt you to use your Passkey to authenticate. Or if your browser supports it, you will be prompted to use your Passkey when you focus the username field.
Django OTP WebAuthn provides its own models for credentials and attestations for those credentials. If these do not suit your needs you can also provide your own models. When using a custom credential model them you must use a custom attestation model as well.
Two abstract base models exist with all of the required fields and methods implemented, django_otp_webauthn.models.AbstractWebAuthnCredential
and django_otp_webauthn.models.AbstractWebAuthnAttestation
. Your custom attestation will need to override the credential
field to related back to your own credential model.
from django.db import models
from django_otp_webauthn.models import AbstractWebAuthnAttestation, AbstractWebAuthnCredential
class MyCredential(AbstractWebAuthnCredential):
pass
class MyAttestation(AbstractWebAuthnAttestation):
credential=models.OneToOneField(MyCredential, on_delete=models.CASCADE, related_name="attestation", editable=False)
The AbstractWebAuthnCredential
model creates an index with a name which includes the concrete model's name with _sha256_idx
appended to the end. If this combination is longer than 30 characters then you will also need to override the index on your credential model to ensure an appropriate length for the index name.
class MyCredentialModelWithALongName(AbstractWebAuthnCredential):
class Meta:
indexes = [
models.Index(fields=["credential_id_sha256"], name="mycredential_id_sha256_idx"),
]
You can also override only the attestation model without any changes to the credential model. When doing so you will still need to implement the credential
field to use a related_name
other than attestation
to avoid a conflict with the related_name
property of the default model.
from django.db import models
from django_otp_webauthn.models import AbstractWebAuthnAttestation
class MyAttestation(AbstractWebAuthnAttestation):
credential=models.OneToOneField("otp_webauthn.WebAuthnCredential", on_delete=models.CASCADE, related_name="swapped_attestation", editable=False)
Passkeys are a new way to authenticate on the web. Officially they are called 'WebAuthn credentials', but Passkeys are the more memorable, human-friendly name, that has been chosen to describe them. They allow users of your site to use their phone, laptop, security key, or other compatible device to authenticate without having to remember a password.
Passkeys follow the WebAuthn standard. The standard describes a way to use public-key cryptography to authenticate users.
Here is an (overly simplified) explanation of how Passkeys work. For a more detailed explanation, try Auth0's interactive WebAuthn demo. It has a very nice explanation of the WebAuthn flow! Or dive into the WebAuthn standard itself.
Passkeys are sometimes claimed to be silver bullet for security. While they are more secure than passwords, they are not perfect.
You put trust in the user's device and its manufacturer. Most devices support some form of syncing Passkeys between devices, like through an iCloud or Google account. This means that if someone gains access to the users' iCloud or Google account, they could potentially access their Passkeys. Users that have poorly secured their account and devices are at risk. However, this is not unique to Passkeys. The same risks exists for password managers and other forms of Multi Factor Authentication that support syncing between devices. Passkeys improve over other methods by their resistance to phishing attacks, credential stuffing and their convenience.
It is the author's opinion that the benefits of Passkeys outweigh the risks. This section is here for your own consideration.
Plenty of websites already support Passkeys. Here are some well known examples:
It is about time for your website to support Passkeys too!
Here are some good resources to learn more about Passkeys:
See DEVELOPMENT.md for information on how to develop and contribute to this project.
This project is licensed under the BSD 3-Clause License. See the LICENSE file for details.
FAQs
FIDO2 WebAuthn support for django-otp: lets your users authenticate with Passkeys
We found that django-otp-webauthn demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.