Passkeys, passkeys, passkeys! Everyone's talking about them. With Amazon rolling out passkeys last year and Google encouraging users to make them the default authentication method, it raises the question: How do I add them to my app?
How do passkeys work?
If you’re just interested in implementing them, you can skip this section. No offense taken 😉
FIDO2 multidevice credentials more often referred to as “passkeys” is a standard introduced by the FIDO alliance. It’s an extremely powerful way of using public-key cryptography to verify the identity of a user without passwords, multi-factor, etc. The public / private key pair is usually generated and securely stored on a hardware or software authenticator device.
To learn more about how passkeys and authenticators work in detail, check out the Secure By Design Hub article on passkeys.
How do I build it in?
In this tutorial, we’re going to leverage Pangea AuthN’s hosted pages to be able to quickly configure passkeys without building all the cryptographic mayhem from scratch 😅. To prove that it’s easy to add passkeys into any application in just a few minutes, I’m going to start with a fresh new Django app and implement passkeys in just a few steps.
Step 1: Create a new Django app and install the required dependencies
python -m pip install django
django-admin startproject mysite
cd mysite
python manage.py startapp authme
pip install python-dotenv pangea-django
python manage.py migrate
Let's go to mysite/mysite/settings.py
to allow our app to use these packages.
We need to be able to:
load a
.env
fileaccess the
pangea-django
middlewareload our HTML pages from a designated
templates
folder
from dotenv import load_dotenv
import os
from pathlib import Path
load_dotenv()
# Skip down to the middlware declaration.
# Comment or remove the default django middleware.
# Add Pangea's Auth Middleware
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
#"django.contrib.auth.middleware.AuthenticationMiddleware",
"pangea_django.PangeaAuthMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
# Replace the default configuration with the templates below
# templates folder is where we will keep our html
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "templates")],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
Step 2: Create an account on pangea.cloud
Head over to pangea.cloud and create an account for free. Then in the developer console, enable the “AuthN” service and grab the following tokens.
These tokens will be pasted in your .env config file as shown below. Create and add these tokens into mysite/.env
file like this:
PANGEA_AUTHN_TOKEN=<PANGEA_AUTHN_TOKEN>
PANGEA_DOMAIN=<PANGEA_DOMAIN>
PANGEA_HOSTED_LOGIN=<PANGEA_HOSTED_LOGIN>
Step 3: Add the Pangea Auth components to the new Django app
Now we edit our mysite/authme/views.py
file with the pangea_django
package that maintains authentication context and state across the application.
So our authme/views.py
file should look like this:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from pangea_django import PangeaAuthentication, generate_state_param
import os
# Create your views here.
def landing(request):
if request.user.is_authenticated:
return redirect("/home")
hosted_login = os.getenv("PANGEA_HOSTED_LOGIN")
redirect_url = hosted_login + "?state=" + generate_state_param(request)
return redirect(redirect_url)
def login_success(request):
user = PangeaAuthentication().authenticate(request=request)
if user:
return redirect("/home")
return redirect("/")
@login_required(login_url="/")
def logout(request):
user = PangeaAuthentication().logout(request)
return redirect("/logout")
@login_required(login_url="/")
def home(request):
context = {}
context["user"] = request.user
return render(request, "home.html", context)
Next we need to map these views to paths. We do this in mysite/mysite/urls.py
from django.urls import path
import authme
urlpatterns = [
path('', authme.views.landing),
path('home', authme.views.home),
path('login_success', authme.views.login_success),
path('logout', authme.views.logout),
]
The app need to be able to access mysite/authme/views.py
from mysite/mysite/urls.py
. Add this line to mysite/authme/__init__.py
from . import views
Lastly we need a page for this authentication to render to. The way we make pages in Django is by creating a templates folder with html pages. Since authme.view.home
is routing to <base url>/home
, we need to call our page mysite/templates/home.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body,h1,h2,h3,h4,h5,h6 {font-family: "Lato", sans-serif;}
body, html {
height: 100%;
color: #b47b00;
line-height: 1.8;
background-color: antiquewhite;
}
table {
margin: 0px auto;
background-color: white;
padding: 25px;
}
tr {
font-family: Verdana, Tahoma, sans-serif;
font-size: 20px;
color: #rgb(158, 108, 0);
}
td {
padding: 2px;
}
</style>
</head>
<body>
<!-- Navbar (sit on top) -->
<div>
<a href="logout">LOGOUT</a>
</div>
<!-- Navbar (sit on top) -->
<h1 style="text-align: center;">You are logged in!</h1>
<table>
<tr>
<td>ID:</td>
<td>{{ user.id }}</td>
</tr>
<tr>
<td>Email:</td>
<td>{{ user.email }}</td>
</tr>
<tr>
<td>First Name:</td>
<td>{{ user.first_name }}</td>
</tr>
<tr>
<td>Last Name:</td>
<td>{{ user.last_name }}</td>
</tr>
<tr>
<td>Last Login:</td>
<td>{{ user.last_login }}</td>
</tr>
</table>
</body>
</html>
Step 4: Add Redirect and Enable Passkeys in Pangea console
We need to add an authorized redirect, so that Pangea’s AuthN hosted pages can successfully redirect us back to the http://127.0.0.1:8000/home when a user is done authenticating.
So first, let’s go under General > Redirect (Callback) Settings
and add http://127.0.0.1:8000/home as a redirect and save it.
Here comes the last step, let’s enable Passkeys! Head over to Single Sign On > Passkeys
and enable it. Optionally you can choose to enable fallback authentication options based on your desired user experience.
Here’s a quick video on how you can enable it in settings:
Let's try it out! You can find the code in Github and watch the demo below.
If you are looking to add AuthN to other languages or frameworks, follow these tutorials: