This tutorial shows how to add passkeys to a Python Django web app. We use Corbado's passkey-first UI component and implement simple session management.
Nicolai
Created: November 30, 2023
Updated: February 17, 2025
We aim to make the Internet a safer place using passkeys. That's why we want to support developers with tutorials on how to implement passkeys.
2. Django Passkey Project Prerequisites
3. Repository Structure for Django Passkey Project
4. Set Up Your Corbado Account and Project
5. Django Passkey Project Setup
5.2. Configure Environment Variables
5.3. Create Templates With Session Management
5.4. Create Django Controller and Configure Routes
In this tutorial, well be walking through the process of building a sample application with passkey authentication using the Django web framework made for Python. To make passkeys work, we use Corbado's passkey-first UI component that automatically connects to a passkeys backend.
If you want to run the project straight away, please follow the README of our sample app Github repository.
The result looks as follows:
Recent Articles
⚙️
FastAPI Passkeys: How to Implement Passkeys with FastAPI
⚙️
Flask Passkeys: How to Implement Passkeys with Python Flask
⚙️
Passkey Tutorial: How to Implement Passkeys in Web Apps
⚙️
Keycloak Passkeys: Add Passkeys To Your Existing Keycloak Users
⚙️
Laravel Passkeys: How to Implement Passkeys in PHP Laravel
This tutorial assumes basic familiarity with Django and Python as well as HTML and JavaScript. Let's dive in!
Our Django project contains many files, but these are the most important ones:
├── .env # Contains all environment variables ├── passkeys_demo | ├── settings.py # Global settings | ├── urls.py # Route config | ├── views.py # Controller for our pages | ├── templates | | ├── index.html # Login page | | └── profile.html # Profile page
Visit the Corbado developer panel to sign up and create your account (you'll see the passkey sign-up in action here!).
After sign-up, a project wizard will guide you through the necessary steps to get everything up and running:
Application URL
and Relying Party ID
. The Application URL
is the URL where you embed the Corbado UI component. In this example, we set it to http://localhost:8000
. The Relying Party ID
is the domain (no protocol, no port and no path) where passkeys should be bound to. Here, it's localhost
(you can define both values als in the Settings > General > URLs of the Corbado developer panel).Afterwards, you'll see the relevant HTML / JavaScript code snippets you need to integrate into the project. The subsequent sections of this article will explain them in detail.
As another step, we create an API secret
which will be needed to request user data from the
Corbado backend. Please create an API secret
in Settings > Credentials > API secrets.
If you haven't installed Django yet, do so by executing
pip install Django==4.2.7
Install the latest version of the Corbado Python SDK (passkeys):
pip install passkeys
To initialize our project, we create a new Django project with
django-admin startproject passkeys_demo
This will create a passkeys_demo
folder containing our Django project.
We head into our project with
cd passkeys_demo
and create a .env
file in the passkeys_demo
subfolder with the following contents (using your own values of
course):
PROJECT_ID=pro-xxx API_SECRET=corbado1_xxx
We use environ to load the key-value pairs from the .env
file into our
environment variables. Install it with
pip install django-environ
Then, we can import it in our passkeys_demo/settings.py
file and use it to
obtain the variables from `.env and add them as actual environment variables in
Django:
import environ env = environ.Env() environ.Env.read_env() PROJECT_ID = env('PROJECT_ID') API_SECRET = env('API_SECRET')
Our templates are placed in passkeys_demo/templates
(You must create the
templates folder first). The login page will be in
passkeys_demo/templates/index.html
. It contains a script from Corbado as well
as the Corbado UI component:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://unpkg.com/@corbado/web-js@latest/dist/bundle/index.css" /> <script src="https://unpkg.com/@corbado/web-js@latest/dist/bundle/index.js"></script> <body> <script> (async () => { await Corbado.load({ projectId: "{{ PROJECT_ID }}", darkMode: "off", setShortSessionCookie: "true", //set short session cookie automatically }); const authElement = document.getElementById('corbado-auth'); //Element where you want to render CorbadoAuth UI Corbado.mountAuthUI(authElement, { onLoggedIn: () => { //post login actions can be performed here. window.location.href = '/profile'; }, }); })(); </script> <div id="corbado-auth"></div> </body> </html>
Our second page is the profile page where the user will be redirected to after
authentication. Here, we show some basic user info which we will obtain in the
controller beforehand (we will create the controller later). We also provide
a logout button which will terminate the session Corbado had initiated after
authentication. The page is located at passkeys_demo/templates/profile.html
with the following content:
<!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://unpkg.com/@corbado/web-js@latest/dist/bundle/index.css" /> <script src="https://unpkg.com/@corbado/web-js@latest/dist/bundle/index.js"></script> </head> <body> <!-- Define passkey-list div and logout button --> <h2>:/protected 🔒</h2> <p>User ID: {{USER_ID}}</p> <p>Name: {{USER_NAME}}</p> <p>Email: {{USER_EMAIL}}</p> <div id="passkey-list"></div> <button id="logoutButton">Logout</button> <!-- Script to load Corbado and mount PasskeyList UI --> <script> (async () => { await Corbado.load({ projectId: "{{ PROJECT_ID }}", darkMode: "off", setShortSessionCookie: "true" // set short session cookie automatically }); // Get and mount PasskeyList UI const passkeyListElement = document.getElementById("passkey-list"); // Element where you want to render PasskeyList UI Corbado.mountPasskeyListUI(passkeyListElement); // Get logout button const logoutButton = document.getElementById('logoutButton'); // Add event listener to logout button logoutButton.addEventListener('click', function() { Corbado.logout() .then(() => { window.location.replace("/"); }) .catch(err => { console.error(err); }); }); })(); </script> </body> </html>
We will now create the controller for the pages we created a step earlier. In
the passkeys_demo
folder create a views.py
file. It needs two methods: one
for the index page and one for the profile page. Inside the index method, we
only need to inject the Project ID
, but inside the profile method, we need to
verify the integrity of the Corbado session and extract the data stored in it.
For this, we use the Corbado Python SDK sessions
service:
The views.py
file should look like this:
from typing import List from corbado_python_sdk import Config, CorbadoSDK, SessionInterface, UserEntity from corbado_python_sdk.entities.session_validation_result import ( SessionValidationResult, ) from corbado_python_sdk.generated.models.identifier import Identifier from corbado_python_sdk.services.interface.identifier_interface import ( IdentifierInterface, ) from django.http import HttpResponse from django.shortcuts import redirect, render from passkeys_demo.settings import API_SECRET, PROJECT_ID # Config has a default values for 'short_session_cookie_name' and 'BACKEND_API' config: Config = Config( api_secret=API_SECRET, project_id=PROJECT_ID, ) # Initialize SDK sdk: CorbadoSDK = CorbadoSDK(config=config) sessions: SessionInterface = sdk.sessions identifiers: IdentifierInterface = sdk.identifiers def index(request) -> HttpResponse: context = { "PROJECT_ID": PROJECT_ID, } return render(request, "index.html", context) def profile( request, ) -> HttpResponse: token = request.COOKIES.get(config.short_session_cookie_name) try: if not token: raise ValueError("No token found") validation_result: SessionValidationResult = ( sessions.get_and_validate_short_session_value(short_session=token) ) if validation_result.authenticated: email_identifiers: List[Identifier] = ( identifiers.list_all_emails_by_user_id( user_id=validation_result.user_id or "" # at this point user_id should be non empty string since user was authenticated ) ) user: UserEntity = sessions.get_current_user(short_session=token) context = { "request": request, "PROJECT_ID": PROJECT_ID, "USER_ID": user.user_id, "USER_NAME": user.full_name, "USER_EMAIL": email_identifiers[0].value, } return render(request, "profile.html", context) else: return HttpResponse( "You are not authenticated or have not yet confirmed your email.", status=401, ) except Exception as e: print(e) return redirect("/")
As a last step, we configure the routes for the two methods. Inside
passkeys_demo/urls.py
, we set a route for the login page and one for the
profile page as well as a redirect for any other route:
from django.urls import path from . import views from django.shortcuts import redirect urlpatterns = [ path("", views.index, name="index"), path("profile/", views.profile, name="profile"), path('<path:unknown_path>/', lambda request, unknown_path: redirect('/'), name='fallback') ]
Now, run your Django application using the following command:
python manage.py runserver
Your Django application will start and when visiting http://localhost:8000 with a web browser, you should see the Corbado UI component:
After successful sign-up / login, you see the profile page:
This tutorial showed how easy it is to add passwordless authentication with passkeys to a Django app with using Corbado. Besides the passkey-first authentication, Corbado provides simple session management, that we used for a retrieval of basic user data. If you want to read more about how Corbado's session management please check the docs here.
Table of Contents
Enjoyed this read?
🤝 Join our Passkeys Community
Share passkeys implementation tips and get support to free the world from passwords.
🚀 Subscribe to Substack
Get the latest news, strategies, and insights about passkeys sent straight to your inbox.
Related Articles
Flask Passkeys: How to Implement Passkeys with Python Flask
Janina - September 15, 2023
FastAPI Passkeys: How to Implement Passkeys with FastAPI
Nicolai - December 19, 2023
Keycloak Passkeys: Add Passkeys To Your Existing Keycloak Users
Nicolai - November 23, 2023