Skip to content
Open

V2.0 #66

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5badc76
Merged final immediate login
mkalioby Mar 25, 2026
880adae
Merged immediate
mkalioby Mar 25, 2026
4a14a34
Add Immediate Mediation steps
mkalioby Mar 25, 2026
6f94a39
Fix README.md
mkalioby Mar 25, 2026
f7185cc
Merge drf
mkalioby Mar 27, 2026
3d2a2e1
Fix tox
mkalioby Mar 27, 2026
9472590
Fixes to tox.ini
mkalioby Mar 27, 2026
735c005
Dropped support django2.0,2.1,3.0,3.1,4.0,4.1,5.0,5.1
mkalioby Mar 28, 2026
8be6293
Generate docs to mkdocs
mkalioby Mar 28, 2026
23ed214
Updated documentation
mkalioby Mar 28, 2026
9e34007
run django6 test starting py3.12
mkalioby Mar 28, 2026
299fa8a
Added Py3.7 and djangorestframework-simplejwt
mkalioby Mar 28, 2026
110ef87
Run Py3.7
mkalioby Mar 28, 2026
df86ee3
Updated coverage.svg
github-actions[bot] Mar 28, 2026
c501b32
Fix API issues, add JWT support, improve example templates and docs (…
ganiyevuz Mar 28, 2026
3a5bf8a
Updated coverage.svg
github-actions[bot] Mar 28, 2026
3c396e6
Fix API issues, add JWT support, improve example templates and docs (…
ganiyevuz Mar 28, 2026
096193b
Fix run_tox
mkalioby Mar 28, 2026
81770a8
Run Py3.7
mkalioby Mar 28, 2026
07d52e2
Split tox files
mkalioby Mar 28, 2026
9c4f56c
fix docs badge link
mkalioby Mar 28, 2026
89ac302
v2.0rc1 released
mkalioby Mar 28, 2026
62fdb50
Update tox.ini
mkalioby Mar 28, 2026
1a40393
Fixed @mahmoudnasr comments
mkalioby Mar 29, 2026
0e7c5e4
Merge branch 'v2.0' of github.com:mkalioby/django-passkeys into v2.0
mkalioby Mar 29, 2026
c4eb116
Updated coverage.svg
github-actions[bot] Mar 29, 2026
258676e
Allow Continue on error for repo push
mkalioby Mar 29, 2026
f5c7ea5
Merge branch 'v2.0' of github.com:mkalioby/django-passkeys into v2.0
mkalioby Mar 29, 2026
c68acd8
Split tox jobs
mkalioby Mar 29, 2026
f8200a2
Fix run_tox.yml
mkalioby Mar 29, 2026
ebb28ba
Update workflow
mkalioby Mar 29, 2026
34f74eb
.github/workflows/run_tox.yml
mkalioby Mar 29, 2026
b3c789b
Fix tox
mkalioby Mar 29, 2026
7fb8b89
add setuptools on top on tox deps
mkalioby Mar 29, 2026
534098a
Make each version a sub job
mkalioby Mar 29, 2026
52ea304
Remove django2.2 from Py3.9
mkalioby Mar 29, 2026
b5e91b7
Change job names
mkalioby Mar 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/basic_checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ jobs:
- name: Push changes
if: steps.verify-changed-files.outputs.files_changed == 'true'
uses: ad-m/github-push-action@master
continue-on-error: true
with:
github_token: ${{ secrets.github_token }}
branch: ${{ github.ref }}
22 changes: 22 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: docs

on:
push:
branches: ["main"]
workflow_dispatch:

permissions:
contents: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install dependencies
run: pip install -r docs/requirements.txt
- name: Deploy docs
run: mkdocs gh-deploy --force
42 changes: 31 additions & 11 deletions .github/workflows/run_tox.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,43 @@ env:
DJANGO_SETTINGS_MODULE: test_app.test_settings

jobs:
tests:
testPy37-39:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: [ '3.7', '3.8', '3.9' ]
name: Python ${{ matrix.python-version }} Run
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install setuptools
continue-on-error: true
run: |
python -m pip install --upgrade pip
python -m pip install setuptools

- name: Install tox
run: |
python -m pip install --upgrade pip
pip install tox
- name: Run tox
run: tox run --conf tox_old.ini
tests310-314:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.10', '3.11', '3.12', '3.13', '3.14' ]
name: Python ${{ matrix.python-version }} Run
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v5
- uses: actions/setup-python@v6
with:
python-version: |
3.8
3.9
3.10
3.11
3.12
3.13
3.14
python-version: ${{ matrix.python-version }}
- name: Install tox
run: |
python -m pip install --upgrade pip
pip install tox
- name: Run tox
run: tox
run: tox run --conf tox.ini
15 changes: 15 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
version: 2

build:
os: "ubuntu-24.04"
tools:
python: "3"
# We recommend using a requirements file for reproducible builds.
# This is just a quick example to get started.
# https://docs.readthedocs.io/page/guides/reproducible-builds.html
jobs:
pre_install:
- pip install mkdocs-material

mkdocs:
configuration: mkdocs.yml
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
## 2.0.0
* Breaking Change: Moved templates to `passkeys` folder and renamed the templates. thanks to @ganiyevuz and @smark-1
* `PassKeys.html` -> `passkeys/manage.html`
* `PassKeys_base.html` -> `passkeys/base.html`
* `check_passkeys.js` -> `passkeys/check.js`
* `passkeys.js` -> `passkeys/passkeys.js`
* `modal.html` -> `passkeys/modal.html`
* Dropped Support for django-2.0, django-2.1, django-4.0, django-4.1, django-5.0, django-5.1, but django 2.2, 3.2.
* New: DRF API module (`passkeys.api`) — REST endpoints for passkey registration, authentication, and management
* New: Pluggable token backend — auto-detects SimpleJWT, DRF TokenAuth, or session-based auth
* New: Service layer (`passkeys.api.service`) — session-independent FIDO2 logic with signed state tokens
* New: Optional install via `pip install django-passkeys[drf]` or `pip install django-passkeys[drf-jwt]`
* Added: Support for Google new WebAuthn immediate mediation API (with allow/disallow password login) for Chromium Browser. for more details check [Google's announcement](https://developer.chrome.com/blog/webauthn-immediate-mediation-ot).
* Fix: add `@login required` to passkey registration views. thanks to @rafaelurbeno for reporting the issue.
* New: Add docs and hosted on [readthedocs.io](https://django-passkeys.readthedocs.io/en/v2.0/)

## 1.4.1
* Add csrfmiddlwwaretoken to deleteKey and toggle key.

Expand Down
119 changes: 42 additions & 77 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,46 @@
[![Downloads / Month ](https://pepy.tech/badge/django-passkeys/month)](https://pepy.tech/project/django-passkeys)
[![build](https://github.com/mkalioby/django-passkeys/actions/workflows/basic_checks.yml/badge.svg)](https://github.com/mkalioby/django-passkeys/actions/workflows/basic_checks.yml)
![Coverage](https://raw.githubusercontent.com/mkalioby/django-passkeys/main/coverage.svg)
[![docs](https://app.readthedocs.org/projects/django-passkeys/badge/?version=v2.0)](https://django-passkeys.readthedocs.io/en/v2.0/)

![Django Versions](https://img.shields.io/pypi/frameworkversions/django/django-passkeys)
![Python Versions](https://img.shields.io/pypi/pyversions/django-passkeys)


An extension to Django *ModelBackend* backend to support passkeys.
An extension to Django *ModelBackend* backend to support passkeys. Supports both django templates and REST API (Django REST Framework) with pluggable token backends (JWT, DRF Token, or Session).

Passkeys is an extension to Web Authentication API that will allow the user to login to a service using another device.

This app is a slim-down version of [django-mfa2](https://github.com/mkalioby/django-mfa2)
This app is a slimmed-down version of [django-mfa2](https://github.com/mkalioby/django-mfa2)

Passkeys are now supported on
Passkeys are now supported on
* Apple Ecosystem (iPhone 16.0+, iPadOS 16.1, Mac OS X Ventura)
* Chromium based browsers (on PC and Laptop) allows picking up credentials from Android and iPhone/iPadOS.
* Android Credentials creation for ResidentKeys is currently live.

On May 3, 2023, Google allowed the use of Passkeys for the users to login, killing the password for enrolled users.
On May 3, 2023, Google allowed the use of Passkeys for the users to login, killing the password for enrolled users.

# Installation
## Special Features

django-passkeys supports the following features:
### 1. Conditional UI
**Conditional UI** is a way for the browser to prompt the user to use the passkey to login as shown.
![conditionalUI.png](docs/imgs%2FconditionalUI.png)

### 2. WebAuthn immediate mediation for frictionless sign-in

**Immediate Mediation** is an extension to WebAuthn API that allows the browser to immediately prompt the
user to use password/passkeys without the need of a login form. This is currently supported by Google Chrome 144+ and soon on Android devices.

You can watch demo presented by Google

[![Watch the video](docs/imgs/immediate.png)](https://developer.chrome.com//static/blog/webauthn-immediate-mediation-ot/video/immediate-mediation-explicit-flow.mp4)

# Quick Start - Common Settings

`pip install django-passkeys`

Supports Django 2.0+, Python 3.7+
Supports Django 2.2+, Python 3.7+

# Usage
1. In your settings.py add the application to your installed apps
Expand Down Expand Up @@ -59,45 +76,11 @@ Supports Django 2.0+, Python 3.7+

* Starting v1.1, `FIDO_SERVER_ID` and/or `FIDO_SERVER_NAME` can be a callable to support multi-tenant web applications, the `request` is passed to the called function.
* `FIDO_SERVER_ID` must match the domain you access the site from. For local development, use `localhost` and access via `http://localhost:8000/` (not `127.0.0.1`).

5. Add passkeys to urls.py
```python

urls_patterns= [
'...',
url(r'^passkeys/', include('passkeys.urls')),
'....',
]
```
6. To match the look and feel of your project, Passkeys includes `base.html` but it needs blocks named `head` & `content` to added its content to it.
**Notes:**

1. You can override `PassKeys_base.html` which is used by `Passkeys.html` so you can control the styling better and current `Passkeys_base.html` extends `base.html`
1. Currently, `PassKeys_base.html` needs jQuery and bootstrap.

7. Somewhere in your app, add a link to 'passkeys:home'
```html
<li><a href="{% url 'passkeys:home' %}">Passkeys</a> </li>
```
8. In your login view, change the authenticate call to include the request as follows

```python
user = authenticate(request, username=request.POST["username"],password=request.POST["password"])
```

8. Finally, In your `login.html`
* Give an id to your login form e.g 'loginForm', the id should be provided when calling `authn` function
* Inside the form, add
```html
<input type="hidden" name="passkeys" id="passkeys"/>
<button class="btn btn-block btn-dark" type="button" onclick="authn('loginForm')"><img src="{% static 'passkeys/imgs/FIDO-Passkey_Icon-White.png' %}" style="width: 24px"></button>
{%include 'passkeys.js' %}
```
For Example, See 'example' app and look at EXAMPLE.md to see how to set it up.

# Detect if user is using passkeys
Once the backend is used, there will be a `passkey` key in request.session.
If the user used a passkey then `request.session['passkey']['passkey']` will be True and the key information will be there like this
If the user used a passkey then `request.session['passkey']['passkey']` will be `True` and the key information will be there like this

```python
{'passkey': True, 'name': 'Chrome', 'id': 2, 'platform': 'Chrome on Apple', 'cross_platform': False}
```
Expand All @@ -107,59 +90,41 @@ If the user didn't use a passkey then it will be set to False
{'passkey':False}
```

By this the basic installation of django-passkeys, your next step depends on whether you want to use the Django Template integration or the REST API (Django REST Framework) integration.

# Check if the user can be enrolled for a platform authenticator
## Choose Your Integration

If you want to check if the user can be enrolled to use a platform authenticator, you can do the following in your main page.

```html
<div id="pk" class="alert alert-info" style="display: none">Your device supports passkeys, <a href="{%url 'passkeys:enroll'%}">Enroll</a> </div>
<script type="text/javascript">
function register_pk()
{
$('#pk').show();
}
{% include 'check_passkeys.js'%}
$(document).ready(check_passkey(true,register_pk))
</script>
```
check_passkey function paramters are as follows
* `platform_authenticator`: if the service requires only a platform authenticator (e.g TouchID, Windows Hello or Android SafetyNet)
* `success_func`: function to call if a platform authenticator is found or if the user didn't login by a passkey
* `fail_func`: function to call if no platform authenticator is found (optional).
django-passkeys supports two integration modes. Pick the one that fits your project:

| | Template-Based | REST API (DRF) |
|---|---|---|
| **Best for** | Server-rendered Django apps | SPAs, mobile apps, headless APIs |
| **Auth flow** | Session-based with Django forms | Token-based (JWT, DRF Token, or Session) |
| **Frontend** | Django templates with jQuery | Any frontend (React, Vue, mobile, etc.) |
| **Setup guide** | [Template Setup](docs/template-setup.md) | [DRF Setup](docs/drf-setup.md) |

## Using Conditional UI

Conditional UI is a way for the browser to prompt the user to use the passkey to login to the system as shown in
Both can coexist in the same project — you can use templates for your web app and the API for your mobile app.

![conditionalUI.png](imgs%2FconditionalUI.png)
## Example Project

Starting version v1.2. you can use Conditional UI by adding the following to your login page

1. Add `webauthn` to autocomplete of the username field as shown below.
```html
<input name="username" placeholder="username" autocomplete="username webauthn">
```
add the following to the page js.

```js
window.onload = checkConditionalUI('loginForm');
```
where `loginForm` is name of your login form.
See the `example` app and [Example.md](docs/Example.md) for a working demo for templates, drf and immediate mediation.

## Security contact information

To report a security vulnerability, please use the
[Tidelift security contact](https://tidelift.com/security).
Tidelift will coordinate the fix and disclosure.

# Contributors
## Contributors
* [mahmoodnasr](https://github.com/mahmoodnasr)
* [jacopsd](https://github.com/jacopsd)
* [jacopsd](https://github.com/jacopsd)
* [gasparbrogueira](https://github.com/gasparbrogueira)
* [pulse-mind](https://github.com/pulse-mind)
* [ashokdelphia](https://github.com/ashokdelphia)
* [offbyone](https://github.com/offbyone)
* [resba](https://github.com/resba)
* [ganiyevuz](https://github.com/ganiyevuz)
* [smark-1](https://github.com/smark-1)
* [ThomasWaldmann-1](https://github.com/ThomasWaldmann)
* [rafaelurben](https://github.com/rafaelurben)
1 change: 1 addition & 0 deletions build_docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python -m mkdocs build --clean --site-dir ./html --config-file mkdocs.yml
2 changes: 1 addition & 1 deletion coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
47 changes: 47 additions & 0 deletions docs/Example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Use of Example project

1. Create virtual env
```shell
python -m venv env
```
2. Activate env
```
source env/bin/activate
```
3. Install requirements
```
pip install -r requirements.txt
```
4. cd to example project
```
cd example
```
5. Run migrations
```
python manage.py migrate
```
6. Create superuser
```
python manage.py createsuperuser
```
7. Start the server
```
python manage.py runserver
```
8. Open `http://localhost:8000/` in your browser

**Important**: Use `localhost` not `127.0.0.1` — WebAuthn requires the domain to match `FIDO_SERVER_ID` in settings.

# Notes for SSL

For passkeys in production, you need to use HTTPS. After the above steps are done:

1. Stop the server
2. Install SSL requirements
```shell
pip install django-sslserver
```
3. Start the SSL server
```shell
python manage.py runsslserver
```
3 changes: 3 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Changelog

See the full changelog on [GitHub](https://github.com/mkalioby/django-passkeys/blob/main/CHANGELOG.md).
Loading
Loading