├── .env.example ├── .gitignore ├── Dockerfile ├── README.md ├── docker-compose.yml ├── main.py ├── requirements.txt └── templates ├── login.html └── profile.html /.env.example: -------------------------------------------------------------------------------- 1 | PROJECT_ID=pro-xxx 2 | API_SECRET=corbado1_xxx -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .env 3 | *.pyc -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Base image 2 | FROM python:latest 3 | 4 | # Set work directory 5 | WORKDIR /app 6 | 7 | # Copy requirements file 8 | COPY requirements.txt . 9 | 10 | # Install dependencies 11 | RUN pip install -r requirements.txt 12 | 13 | # Copy .env file (including credentials) and project code 14 | COPY . . 15 | 16 | # Expose port 17 | EXPOSE 3000 18 | 19 | # Run app 20 | CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "3000"] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FastAPI Passkey Example App 2 | 3 | ## Project Overview 4 | 5 | This project implements a web application with a login using Corbado's passkey-first authentication service 6 | integrated with a FastAPI backend. The application consists of two main pages: a login page and a home page. Once users 7 | log in successfully via the Corbado on the login page, they are redirected to the home page where they can log 8 | out and view protected content. 9 | 10 | Please see the [full blog post](https://www.corbado.com/blog/passkeys-fastapi) to understand all the required steps to integrate passkeys into FastAPI apps. 11 | 12 | 13 | ## Tools and Technologies Used 14 | 15 | - **FastAPI**: A full-featured Python web framework, used to build the backend of the application. 16 | - **Corbado**: An authentication service used to handle passkey-first user authentication. 17 | - **HTML & CSS**: Used to structure and style the frontend of the application. 18 | 19 | ## Features 20 | 21 | - **Passkey-first Authentication**: Utilizes Corbado's authentication service for secure user login. 22 | - **Session management**: Uses Corbado's session management to display content based on the user's authentication status. 23 | 24 | ## How to Use 25 | 26 | ### 1. File structure 27 | ``` 28 | ├── .env # Contains all environment variables 29 | ├── main.py # Contains our webapplication (Handles routes) 30 | └── templates 31 | ├── index.html # Login page 32 | └── profile.html # Profile page 33 | ``` 34 | 35 | ### 2. Setup 36 | #### Step 2.1: Clone the Repository 37 | 38 | Clone this repository to your local machine by running: 39 | 40 | ```sh 41 | git clone https://github.com/corbado/example-passkeys-fastapi 42 | 43 | ``` 44 | 45 | #### Step 2.2: Create .env File 46 | 47 | To configure the credentials, you will need to create a `.env` file with your Corbado `Project ID` and `API secret`: 48 | To get your `Project ID` and `API secret`, visit the [Corbado developer panel](https://app.corbado.com/?technology=passkeys&framework=FastAPI#signup-init). 49 | 50 | Please refer to the [Corbado docs](https://docs.corbado.com/overview/welcome) for more details on obtaining the 51 | necessary credentials and integrating Corbado authentication in your application. 52 | 53 | ```sh 54 | PROJECT_ID= 55 | API_SECRET= 56 | 57 | ``` 58 | 59 | #### Step 2.3: Configure Corbado project 60 | 61 | In the Corbado developer panel, visit the [URLs settings](https://app.corbado.com/app/settings/general/urls) and enter the values shown in the image below: 62 | ![Corbado Developer Panel FastAPI URLs](https://github.com/user-attachments/assets/9fdc7edc-2bba-4a82-880b-27a931b84e03) 63 | 64 | 65 | #### Step 2.4: Run the Project 66 | 67 | Use the following command to run the project in a docker container: 68 | 69 | ```sh 70 | docker compose up 71 | ``` 72 | 73 | ### 3. Usage 74 | 75 | After step [2.4](#step-24-run-the-project), your local server should be fully working. 76 | 77 | If you now visit [http://localhost:3000](http://localhost:3000), you should be able to sign up using the Corbado UI component. 78 | 79 | fastapi passkeys ui component 80 | 81 | When authenticated you will be forwarded to the `/profile` page. 82 | 83 | fastapi passkey list 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | fastapi: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile 8 | env_file: 9 | - ./.env 10 | ports: 11 | - 3000:3000 12 | restart: always -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from corbado_python_sdk.entities.session_validation_result import ( 3 | SessionValidationResult, 4 | ) 5 | from corbado_python_sdk.generated.models.identifier import Identifier 6 | 7 | from fastapi import FastAPI, Request, Response 8 | from fastapi.responses import HTMLResponse 9 | from fastapi.templating import Jinja2Templates 10 | from dotenv import load_dotenv 11 | import os 12 | from corbado_python_sdk import ( 13 | Config, 14 | CorbadoSDK, 15 | ) 16 | from corbado_python_sdk import SessionService, IdentifierService, UserService 17 | 18 | load_dotenv() 19 | app = FastAPI() 20 | 21 | templates = Jinja2Templates(directory="templates") 22 | 23 | PROJECT_ID: str = os.getenv("PROJECT_ID", "pro-xxx") 24 | API_SECRET: str = os.getenv("API_SECRET", "corbado1_xxx") 25 | 26 | # Config has a default values for 'short_session_cookie_name' and 'BACKEND_API' 27 | config: Config = Config( 28 | api_secret=API_SECRET, 29 | project_id=PROJECT_ID, 30 | ) 31 | 32 | # Initialize SDK 33 | sdk: CorbadoSDK = CorbadoSDK(config=config) 34 | sessions: SessionService = sdk.sessions 35 | identifiers: IdentifierService = sdk.identifiers 36 | 37 | 38 | @app.get("/", response_class=HTMLResponse) 39 | async def get_login(request: Request): 40 | return templates.TemplateResponse( 41 | "login.html", {"request": request, "PROJECT_ID": PROJECT_ID} 42 | ) 43 | 44 | 45 | @app.get("/profile", response_class=HTMLResponse) 46 | async def get_profile(request: Request): 47 | # Acquire cookies with your preferred method 48 | sessionToken: str = request.cookies.get("cbo_session_token") or "" 49 | validation_result: SessionValidationResult = ( 50 | sessions.get_and_validate_short_session_value(short_session=sessionToken) 51 | ) 52 | if validation_result.authenticated: 53 | 54 | emailList: List[Identifier] = identifiers.list_all_emails_by_user_id( 55 | user_id=validation_result.user_id 56 | or "" # at this point user_id should be non empty string since user was authenticated 57 | ) 58 | 59 | context = { 60 | "request": request, 61 | "PROJECT_ID": PROJECT_ID, 62 | "USER_ID": validation_result.user_id, 63 | "USER_NAME": validation_result.full_name, 64 | "USER_EMAIL": emailList[0].value, 65 | } 66 | return templates.TemplateResponse("profile.html", context) 67 | 68 | else: 69 | return Response( 70 | content="You are not authenticated or have not yet confirmed your email.", 71 | status_code=401, 72 | ) 73 | 74 | 75 | if __name__ == "__main__": 76 | import uvicorn 77 | 78 | uvicorn.run(app, host="0.0.0.0", port=3000, reload=True) 79 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | passkeys>=1.1.3 3 | Jinja2 4 | uvicorn 5 | python-dotenv -------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /templates/profile.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 13 |

:/protected 🔒

14 |

User ID: {{USER_ID}}

15 |

Name: {{USER_NAME}}

16 |

Email: {{USER_EMAIL}}

17 |
18 | 19 | 20 | 21 | 22 | 47 | 48 | 49 | 50 | 51 | --------------------------------------------------------------------------------