django-otp-webauthn


Namedjango-otp-webauthn JSON
Version 0.1.1 PyPI version JSON
download
home_pageNone
SummaryFIDO2 WebAuthn support for django-otp: lets your users authenticate with Passkeys
upload_time2024-05-26 18:51:45
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords authentication django django-otp fido2 mfa security u2f webauthn
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Django OTP WebAuthn

This package provides an implementation of [WebAuthn Passkeys](https://passkeys.dev/) for Django. It is written as a plugin for the [Django OTP framework](https://github.com/django-otp/django-otp) for multi-factor authentication. Under the hood, this package uses [py_webauth](https://github.com/duo-labs/py_webauthn/) to handle all cryptographic operations.

> [!IMPORTANT]  
> This package is still in development and is not yet ready for production use. 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](DEVELOPMENT.md) file.

- [Django OTP WebAuthn](#django-otp-webauthn)
  - [Compatibility](#compatibility)
    - [Browser compatibility](#browser-compatibility)
  - [Features](#features)
  - [Quick start guide - how to use Passkeys in your Django project](#quick-start-guide---how-to-use-passkeys-in-your-django-project)
  - [What exactly is a Passkey?](#what-exactly-is-a-passkey)
    - [How Passkeys work (in a nutshell)](#how-passkeys-work-in-a-nutshell)
    - [Why use Passkeys?](#why-use-passkeys)
    - [A note about security](#a-note-about-security)
  - [Who uses Passkeys?](#who-uses-passkeys)
  - [Further reading](#further-reading)
  - [Development](#development)
  - [License](#license)

## Compatibility

- Django >= 4.2
- Python >= 3.9
- django-otp >= 1.2.0

### Browser compatibility

Passkeys are supported in most modern browsers. Here is a list of browsers that support Passkeys:

- Chrome 67+
- Firefox 60+
- Safari 13+
- Microsoft Edge 18+

For a complete list, see [caniuse.com/webauthn](https://caniuse.com/webauthn).

## Features

- **Passkeys as a second factor.** Lets users just click yes on the browser prompt to verify their identity after they have entered their password.
- **Passwordless login with Passkeys (optional).** Lets users verify their identity using a biometric sensor, security key, or other compatible device. Can be disabled if you prefer to use Passkeys as a second factor only.
- **Batteries included.** comes with a default frontend JavaScript implementation that works out of the box and removes complexity for you.
- **Flexible frontend.** you can style the fronted implementation to fit your brand. Or roll your own frontend implementation if you need something more custom.
- **Compatible with strict [Content Security Policy (CSP)](https://content-security-policy.com/).** The frontend implementation does not rely on inline scripts and is compatible with strict CSP settings.

## Quick start guide - how to use Passkeys in your Django project

To quickly start using Passkeys in your Django project, follow these steps:

1. Install the package from PyPI:

   ```bash
   pip install django-otp-webauthn
   ```

2. Add `django_otp_webauthn` to your `INSTALLED_APPS` in your Django settings:

   ```python
   INSTALLED_APPS = [
       ...
       "django_otp_webauthn",
       ...
   ]
   ```

3. Add the required URLs to your Django project:

   ```python
   # urls.py

   from django.urls import include, path

   urlpatterns = [
       ...
       path("webauthn/", include("django_otp_webauthn.urls", namespace="otp_webauthn")),
       ...
   ]
   ```

4. 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.

   ```python
   # 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 = "localhost"
   # 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 = ["http://localhost"]

   ```

5. Add the registration code to your logged-in user template.

   ```html
   <!-- 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 %}
   ```

6. On your login page, include the following to enable passwordless login:

   ```html
   {% 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.
       {% endcomment %}
       {% render_otp_webauthn_auth_scripts username_field_selector="input[name='username'] %}
   </form>
   ```

7. Don't forget to run migrations:

   ```sh
   python manage.py migrate
   ```

8. 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.

## What exactly is a Passkey?

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](https://www.w3.org/TR/webauthn-3/) standard. The standard describes a way to use [public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) to authenticate users.

### How Passkeys work (in a nutshell)

Here is an (overly simplified) explanation of how Passkeys work. For a more detailed explanation, try [Auth0's interactive WebAuthn demo](https://webauthn.me/). It has a very nice explanation of the WebAuthn flow! Or dive into the [WebAuthn standard](https://www.w3.org/TR/webauthn-3/) itself.

1. An already authenticated user registers a Passkey with your site. A public-private key pair is generated on the user's device. The private key is stored securely and the public key is sent to the server and associated with the authenticated user. An additional piece of information is also stored on the server, called the 'credential ID'.
2. When a user wants to authenticate, the server sends a challenge to the user's device. The user's device signs the challenge with the private key and sends the signature back to the server along with the credential ID.
3. The server looks up the public key associated with the given credential ID and uses it to check the signature. Was this signature generated by the private key that belongs to the public key we have on file? If yes, the user must be in possession of the private key and is authenticated.

### Why use Passkeys?

- **Security.** Compared to passwords, Passkeys are resistant to phishing attacks, credential stuffing, and other common attacks.
- **Convenience.** Passkeys are more convenient than passwords. Users don't have to choose and remember a password, they can use their phone, laptop, or security key to authenticate. Compared to other traditional forms of Multi Factor Authentication, there is no need to wait for an SMS code to arrive or copy a code from an authenticator app. Just click yes on the browser prompt.

### A note about security

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 is 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.

## Who uses Passkeys?

Plenty of websites already support Passkeys. Here are some well known examples:

- [GitHub](https://docs.github.com/en/authentication/authenticating-with-a-passkey/signing-in-with-a-passkey)
- [Google](https://support.google.com/accounts/answer/13548313?hl=en)
- [Microsoft](https://support.microsoft.com/en-us/account-billing/set-up-a-security-key-as-your-verification-method-2911cacd-efa5-4593-ae22-e09ae14c6698)
- [Whatsapp](https://www.threads.net/@wcathcart/post/Cyd27d7pex8)

It is about time for your website to support Passkeys too!

## Further reading

Here are some good resources to learn more about Passkeys:

- [Passkeys.dev](https://passkeys.dev/) - general information about Passkeys
- [Auth0's WebAuthn demo](https://webauthn.me/) - has a very nice explanation of the WebAuthn flow!
- [The WebAuthn standard (working draft)](https://www.w3.org/TR/webauthn-3/)

## Development

See [DEVELOPMENT.md](DEVELOPMENT.md) for information on how to develop and contribute to this project.

## License

This project is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for details.

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "django-otp-webauthn",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "authentication, django, django-otp, fido2, mfa, security, u2f, webauthn",
    "author": null,
    "author_email": "\"Storm B. Heg\" <storm@stormbase.digital>",
    "download_url": "https://files.pythonhosted.org/packages/62/ed/bad73bd12382cc51fe00168f66ed0c9afb3698b08196fcb589549b3765d4/django_otp_webauthn-0.1.1.tar.gz",
    "platform": null,
    "description": "# Django OTP WebAuthn\n\nThis package provides an implementation of [WebAuthn Passkeys](https://passkeys.dev/) for Django. It is written as a plugin for the [Django OTP framework](https://github.com/django-otp/django-otp) for multi-factor authentication. Under the hood, this package uses [py_webauth](https://github.com/duo-labs/py_webauthn/) to handle all cryptographic operations.\n\n> [!IMPORTANT]  \n> This package is still in development and is not yet ready for production use. 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](DEVELOPMENT.md) file.\n\n- [Django OTP WebAuthn](#django-otp-webauthn)\n  - [Compatibility](#compatibility)\n    - [Browser compatibility](#browser-compatibility)\n  - [Features](#features)\n  - [Quick start guide - how to use Passkeys in your Django project](#quick-start-guide---how-to-use-passkeys-in-your-django-project)\n  - [What exactly is a Passkey?](#what-exactly-is-a-passkey)\n    - [How Passkeys work (in a nutshell)](#how-passkeys-work-in-a-nutshell)\n    - [Why use Passkeys?](#why-use-passkeys)\n    - [A note about security](#a-note-about-security)\n  - [Who uses Passkeys?](#who-uses-passkeys)\n  - [Further reading](#further-reading)\n  - [Development](#development)\n  - [License](#license)\n\n## Compatibility\n\n- Django >= 4.2\n- Python >= 3.9\n- django-otp >= 1.2.0\n\n### Browser compatibility\n\nPasskeys are supported in most modern browsers. Here is a list of browsers that support Passkeys:\n\n- Chrome 67+\n- Firefox 60+\n- Safari 13+\n- Microsoft Edge 18+\n\nFor a complete list, see [caniuse.com/webauthn](https://caniuse.com/webauthn).\n\n## Features\n\n- **Passkeys as a second factor.** Lets users just click yes on the browser prompt to verify their identity after they have entered their password.\n- **Passwordless login with Passkeys (optional).** Lets users verify their identity using a biometric sensor, security key, or other compatible device. Can be disabled if you prefer to use Passkeys as a second factor only.\n- **Batteries included.** comes with a default frontend JavaScript implementation that works out of the box and removes complexity for you.\n- **Flexible frontend.** you can style the fronted implementation to fit your brand. Or roll your own frontend implementation if you need something more custom.\n- **Compatible with strict [Content Security Policy (CSP)](https://content-security-policy.com/).** The frontend implementation does not rely on inline scripts and is compatible with strict CSP settings.\n\n## Quick start guide - how to use Passkeys in your Django project\n\nTo quickly start using Passkeys in your Django project, follow these steps:\n\n1. Install the package from PyPI:\n\n   ```bash\n   pip install django-otp-webauthn\n   ```\n\n2. Add `django_otp_webauthn` to your `INSTALLED_APPS` in your Django settings:\n\n   ```python\n   INSTALLED_APPS = [\n       ...\n       \"django_otp_webauthn\",\n       ...\n   ]\n   ```\n\n3. Add the required URLs to your Django project:\n\n   ```python\n   # urls.py\n\n   from django.urls import include, path\n\n   urlpatterns = [\n       ...\n       path(\"webauthn/\", include(\"django_otp_webauthn.urls\", namespace=\"otp_webauthn\")),\n       ...\n   ]\n   ```\n\n4. 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.\n\n   ```python\n   # settings.py\n\n   # The name of the relying party (RP). This is sometimes shown to the user when they register a Passkey.\n   OTP_WEBAUTHN_RP_NAME = \"My Website Inc.\"\n   # This is necessary to bind the Passkey to a specific domain. This should be the domain of your website.\n   OTP_WEBAUTHN_RP_ID = \"localhost\"\n   # 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.\n   # The origins must always be a subdomain of the RP ID or the RP ID itself.\n   OTP_WEBAUTHN_ALLOWED_ORIGINS = [\"http://localhost\"]\n\n   ```\n\n5. Add the registration code to your logged-in user template.\n\n   ```html\n   <!-- logged_in_template.html -->\n   {% load otp_webauthn %}\n\n   {% comment %}\n   This template is displayed when WebAuthn registration is supported.\n   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`.\n   {% endcomment %}\n   <template id=\"passkey-registration-available-template\">\n       <div>\n           <button type=\"button\" id=\"passkey-register-button\">Register Passkey</button>\n           <div id=\"passkey-register-status-message\"></div>\n       </div>\n   </template>\n\n   {% comment %}\n   This template is displayed when WebAuthn registration is not supported.\n   {% endcomment %}\n   <template id=\"passkey-registration-unavailable-template\">\n       <p>Sorry, your browser has no Passkey support</p>\n   </template>\n\n   {% comment %}\n   This placeholder element will be replaced with either the contents of the `passkey-registration-available-template` or the `passkey-registration-unavailable-template` template.\n   {% endcomment %}\n   <span id=\"passkey-registration-placeholder\"></span>\n\n   {% comment %}\n   This template tag renders all the necessary <script> tags for the default registration implementation\n   {% endcomment %}\n   {% render_otp_webauthn_register_scripts %}\n   ```\n\n6. On your login page, include the following to enable passwordless login:\n\n   ```html\n   {% load otp_webauthn %}\n\n   <form method=\"post\">\n       {# Suppose there is an username field on your page that has CSS selector: input[name=\"username\"] #}\n       <label for=\"id_username\">Username</label>\n       <input id=\"id_username\" type=\"text\" name=\"username\" autocomplete=\"username\">\n       {# Other fields omitted for brevity #}\n\n       {# This placeholder element will be replaced with either the contents of the `passkey-verification-available-template` or the `passkey-verification-unavailable-template` template. #}\n       <span id=\"passkey-verification-placeholder\"></span>\n\n       {% comment %}\n       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.\n       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`.\n       {% endcomment %}\n       <template id=\"passkey-verification-available-template\">\n           <button type=\"button\" id=\"passkey-verification-button\">Login using a Passkey</button>\n           <div id=\"passkey-verification-status-message\"></div>\n       </template>\n\n\n       {% comment %}\n       This template is displayed when WebAuthn is not supported.\n       {% endcomment %}\n       <template id=\"passkey-verification-unavailable-template\">\n           <p>Sorry, your browser has no Passkey support</p>\n       </template>\n\n       {% comment %}\n       This template tag renders all the necessary <script> tags for the default verification implementation\n\n       To make browsers automatically suggest a Passkey when you focus the username\n       field, make sure `username_field_selector` is a valid CSS selector.\n       {% endcomment %}\n       {% render_otp_webauthn_auth_scripts username_field_selector=\"input[name='username'] %}\n   </form>\n   ```\n\n7. Don't forget to run migrations:\n\n   ```sh\n   python manage.py migrate\n   ```\n\n8. 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.\n\n## What exactly is a Passkey?\n\nPasskeys 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.\n\nPasskeys follow the [WebAuthn](https://www.w3.org/TR/webauthn-3/) standard. The standard describes a way to use [public-key cryptography](https://en.wikipedia.org/wiki/Public-key_cryptography) to authenticate users.\n\n### How Passkeys work (in a nutshell)\n\nHere is an (overly simplified) explanation of how Passkeys work. For a more detailed explanation, try [Auth0's interactive WebAuthn demo](https://webauthn.me/). It has a very nice explanation of the WebAuthn flow! Or dive into the [WebAuthn standard](https://www.w3.org/TR/webauthn-3/) itself.\n\n1. An already authenticated user registers a Passkey with your site. A public-private key pair is generated on the user's device. The private key is stored securely and the public key is sent to the server and associated with the authenticated user. An additional piece of information is also stored on the server, called the 'credential ID'.\n2. When a user wants to authenticate, the server sends a challenge to the user's device. The user's device signs the challenge with the private key and sends the signature back to the server along with the credential ID.\n3. The server looks up the public key associated with the given credential ID and uses it to check the signature. Was this signature generated by the private key that belongs to the public key we have on file? If yes, the user must be in possession of the private key and is authenticated.\n\n### Why use Passkeys?\n\n- **Security.** Compared to passwords, Passkeys are resistant to phishing attacks, credential stuffing, and other common attacks.\n- **Convenience.** Passkeys are more convenient than passwords. Users don't have to choose and remember a password, they can use their phone, laptop, or security key to authenticate. Compared to other traditional forms of Multi Factor Authentication, there is no need to wait for an SMS code to arrive or copy a code from an authenticator app. Just click yes on the browser prompt.\n\n### A note about security\n\nPasskeys are sometimes claimed to be silver bullet for security. While they are more secure than passwords, they are not perfect.\n\nYou put trust in the user's device and its manufacturer. Most devices support some form is 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.\n\nIt is the author's opinion that the benefits of Passkeys outweigh the risks. This section is here for your own consideration.\n\n## Who uses Passkeys?\n\nPlenty of websites already support Passkeys. Here are some well known examples:\n\n- [GitHub](https://docs.github.com/en/authentication/authenticating-with-a-passkey/signing-in-with-a-passkey)\n- [Google](https://support.google.com/accounts/answer/13548313?hl=en)\n- [Microsoft](https://support.microsoft.com/en-us/account-billing/set-up-a-security-key-as-your-verification-method-2911cacd-efa5-4593-ae22-e09ae14c6698)\n- [Whatsapp](https://www.threads.net/@wcathcart/post/Cyd27d7pex8)\n\nIt is about time for your website to support Passkeys too!\n\n## Further reading\n\nHere are some good resources to learn more about Passkeys:\n\n- [Passkeys.dev](https://passkeys.dev/) - general information about Passkeys\n- [Auth0's WebAuthn demo](https://webauthn.me/) - has a very nice explanation of the WebAuthn flow!\n- [The WebAuthn standard (working draft)](https://www.w3.org/TR/webauthn-3/)\n\n## Development\n\nSee [DEVELOPMENT.md](DEVELOPMENT.md) for information on how to develop and contribute to this project.\n\n## License\n\nThis project is licensed under the BSD 3-Clause License. See the [LICENSE](LICENSE) file for details.\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "FIDO2 WebAuthn support for django-otp: lets your users authenticate with Passkeys",
    "version": "0.1.1",
    "project_urls": {
        "Changelog": "https://github.com/Stormbase/django-otp-webauthn/blob/main/CHANGELOG.md",
        "Issues": "https://github.com/Stormbase/django-otp-webauthn/issues",
        "Source": "https://github.com/Stormbase/django-otp-webauthn"
    },
    "split_keywords": [
        "authentication",
        " django",
        " django-otp",
        " fido2",
        " mfa",
        " security",
        " u2f",
        " webauthn"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "fccc206d68834f55f71b680e0a4b96e7249b24b637292297e0bfce6446e089b4",
                "md5": "7df218a3d66bd4beeb573fe12dd69dab",
                "sha256": "94925590a371e14eeca89c74283b106adb95bbd4a724874eea5ec23187bac1e8"
            },
            "downloads": -1,
            "filename": "django_otp_webauthn-0.1.1-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "7df218a3d66bd4beeb573fe12dd69dab",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 66093,
            "upload_time": "2024-05-26T18:51:47",
            "upload_time_iso_8601": "2024-05-26T18:51:47.043261Z",
            "url": "https://files.pythonhosted.org/packages/fc/cc/206d68834f55f71b680e0a4b96e7249b24b637292297e0bfce6446e089b4/django_otp_webauthn-0.1.1-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "62edbad73bd12382cc51fe00168f66ed0c9afb3698b08196fcb589549b3765d4",
                "md5": "0a184bede68e95446a89ffb8c7c62757",
                "sha256": "b31235584f07b1531eae6bab3d2cc8bcb938ca189f08e9dc6c352119fca07617"
            },
            "downloads": -1,
            "filename": "django_otp_webauthn-0.1.1.tar.gz",
            "has_sig": false,
            "md5_digest": "0a184bede68e95446a89ffb8c7c62757",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 89442,
            "upload_time": "2024-05-26T18:51:45",
            "upload_time_iso_8601": "2024-05-26T18:51:45.262275Z",
            "url": "https://files.pythonhosted.org/packages/62/ed/bad73bd12382cc51fe00168f66ed0c9afb3698b08196fcb589549b3765d4/django_otp_webauthn-0.1.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-05-26 18:51:45",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Stormbase",
    "github_project": "django-otp-webauthn",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "django-otp-webauthn"
}
        
Elapsed time: 0.25264s