Passkeys in Python Flask: Secure your Flask applications with passkeys by following this tutorial for Python Flask developers.
Janina
Created: September 15, 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.
Passkeys are emerging as a passwordless and more secure way to log into websites. In this tutorial, we will learn how to use the Corbado UI components to implement passkey login functionality for a great user experience. Using Python and the Flask framework, we will build a login page that accepts passkeys to authenticate users. With just a few lines of code, you can add this cutting-edge login method to enable fast, simple, and secure access to your web app.
2. Python passkey project prerequisites
3. Repository structure for Python passkey project
5. Configure Corbado for passkey authentication
5.1. Create your Corbado account and project
5.2. Configure environment variables
5.3. Create Flask routes and load environment variables
5.4. Create templates with session management
The image below provides a preview of the passkey login page we will create by following the step-by-step instructions in this tutorial:
This tutorial is suitable for both beginners and experienced developers. To follow along, you'll need to have the following:
pip
installedFor reference, the code snippet below outlines the directory structure of the Flask
passkey app we will be coding in this tutorial. You can grab the full
source code for the complete project from this GitHub URL. Copy the
.env.example
file and rename it to .env
.
/FLASK-CORBADO /templates login.html home.html .env corbado-auth.py
Explanation:
/templates
: This folder will contain the HTML templates for your login and home pages..env
: This file will securely store the Corbado project ID.corbado-auth.py
: This is the main file where your Flask application will be defined.Follow the steps below to set up your project:
mkdir example-passkeys-python-flask-main
cd example-passkeys-python-flask-main
pip
is installed by running:pip --version
pip
to install
Flask, python-dotenv, Corbado Python SDK (which is called passkeys
within pip
):pip install flask python-dotenv passkeys
This will install:
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:5000
. The Relying Party ID
is the domain (no protocol, no port, and no path) where passkeys should be bound. Here, it's localhost
(you can define both values als in the Settings > General > URLs of the Corbado developer panel).Afterwards, you will 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.
In the .env
file, you will store sensitive credentials like your API secret
and project ID as environment variables. This is a security best practice to
avoid hardcoding these values directly in your scripts. To get your API secret
and project ID, visit your Corbado developer panel.
Please refer to the Corbado docs for more details on obtaining the necessary credentials and integrating Corbado authentication in your application.
.envAPI_SECRET=enter_api_key PROJECT_ID=enter_project_id
Create the corbado-auth.py
file, which holds our Flask app that integrates
with Corbado for authentication. In this code, we loaded credentials from
environment variables using python-dotenv
. A Session
class validates JWTs from
cookies to determine the current user. The /
route displays a login page,
while /home
shows the user's profile if authenticated, else raises
Unauthorized
.
Routes are protected by validating JWTs which are short-lived. JWT
signatures are verified using public keys from a JWKS URI. The Session
class
handles JWT validation and protected routes grant access only to valid users. We use Corbado Python SDK
(passkeys) to handle the session.
corbado-auth.pyfrom typing import List from corbado_python_sdk.entities.session_validation_result import ( SessionValidationResult, ) from corbado_python_sdk.generated.models.identifier import Identifier from flask import Flask, render_template, request from werkzeug.exceptions import Unauthorized from dotenv import load_dotenv import os from corbado_python_sdk import ( Config, CorbadoSDK, IdentifierInterface, UserEntity, SessionInterface, UserInterface, ) # Load environment variables from the .env file load_dotenv() app = Flask(__name__) PROJECT_ID: str = os.environ.get("PROJECT_ID") or "" API_SECRET: str = os.environ.get("API_SECRET") or "" # 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 users: UserInterface = sdk.users # Use the API_SECRET from the environment variables app.config["API_SECRET"] = API_SECRET # Pass PROJECT_ID as a context variable to templates app.config["PROJECT_ID"] = PROJECT_ID @app.route("/") def login() -> str: return render_template( template_name_or_list="login.html", PROJECT_ID=app.config["PROJECT_ID"] ) @app.route("/home") def home() -> str: # Acquire cookies with your preferred method token: str = request.cookies.get(config.short_session_cookie_name) or "" validation_result: SessionValidationResult = sessions.get_current_user( short_session=token ) if validation_result.authenticated: user: UserEntity = users.get(user_id=validation_result.user_id) email_identifiers: List[Identifier] = identifiers.list_all_emails_by_user_id( user_id=validation_result.user_id ) user_data = { "id": user.user_id, "name": user.full_name, "email": email_identifiers[0].value, } return render_template( template_name_or_list="home.html", user_data=user_data, PROJECT_ID=app.config["PROJECT_ID"], ) else: raise Unauthorized() if __name__ == "__main__": app.run(host="0.0.0.0", port=5000)
Now, create HTML templates for the login and home pages. The project ID will
be dynamically inserted into these templates using the Jinja2
template engine,
which is integrated with Flask.
Copy and paste these codes in the templates/login.html
file to create the login
page using the Corbado UI component:
templates/login.html<!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 = '/home'; }, }); })(); </script> <div id="corbado-auth"></div> </body> </html>
Now, we create our protected home page, visible only after user authentication
and validation. It showcases user details and incorporates the Corbado Python SDK
(passkeys) for
authentication, allowing users to log out with a simple button click.
templates/home.html<!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_data.id }}</p> <p>Name: {{ user_data.name }}</p> <p>Email: {{ user_data.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 the 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>
In both templates, the PROJECT_ID
is dynamically inserted using {{ PROJECT_ID }}
to include the project ID from the environment variables.
Now, run your Flask application using the following command in the terminal:
python corbado-auth.py
Your Flask application will start, and you can open a web browser to visit http://localhost:5000 to see your login page.
After a successful login and session validation, you should be redirected to the home page where you can see some user data and the logout button as shown in the image above.
Implementing passkey authentication creates a seamless and secure login experience for users, removing the vulnerabilities of passwords. This tutorial showed how to easily integrate passkeys into a Flask app using Corbado.
With just a few lines of Python code, we built a login page that accepts passkeys for authentication. Passkeys represent are the new standard of login, eliminating phishing and breaches tied to reused passwords. By following this guide, you can add cutting-edge passkey support to your own Flask apps and take a step towards a passwordless web.
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.