Python SDK
SDKs and APIs
FastAPI is a modern Python web framework known for its speed, simplicity, and support for asynchronous programming. It’s comparable in performance to APIs built with Go or Node.js—thanks to its underlying tools like Starlette and Pydantic, and its use of ASGI (Asynchronous Server Gateway Interface) rather than WSGI used by frameworks like Flask and Django.
In real-world benchmarks, FastAPI outperforms traditional Python frameworks in both startup time and request handling.
Source: Why FastAPI? by A. Martin, Medium
FastAPI also makes it easy to write asynchronous endpoints, allowing for faster I/O-bound operations like fetching data from external APIs.
In this guide, you’ll build a FastAPI project from scratch, run it locally, and test basic endpoints. Then, you’ll integrate Kinde Auth to securely protect your routes and manage user authentication with ease. Let’s get started!
Run the following command in your terminal window to create a project directory (e.g: kinde-fastapi) and go to it:
mkdir kinde-fastapi && cd kinde-fastapiCreate a virtual Python environment with the following command:
This will create a virtual environment .venv under your project directory. This will install all project dependencies under this folder instead of your global Python directory.
python -m venv .venvActivate the virtual environment using the following command:
source .venv/bin/activateInstall the project dependencies with the following bash command:
pip install kinde_python_sdk fastapi python-dotenv starlette "uvicorn[standard]"Create the app.py file:
touch app.pyOpen the app.py with your favorite code editor and enter the following code:
The code:
/ for the root route, and a dynamic /items/{item_id} routefrom typing import Union from fastapi import FastAPI app = FastAPI() @app.get("/")def read_root(): return {"Hello": "World"} @app.get("/items/{item_id}")def read_item(item_id: int, q: Union[str, None] = None): return {"item_id": item_id, "q": q}Test the app with the following terminal command:
This will spin up an HTTP server and run the app under localhost port 8000
uvicorn app:app --reloadGo to http://localhost:8000 to find the root route:
{ "Hello": "World"}Go to http://localhost:8000/items/420 to find the items endpoint:
{ "item_id": 420, "q": null}Currently, you can access all the routes in your application.
In the coming steps, we will add Kinde authentication to secure the routes.
http://localhost:8000/api/auth/kinde_callback and Save.Create a new configuration file config.py:
touch config.pyOpen config.py with your favorite code editor, enter the following code, and save:
from kinde_sdk.kinde_api_client import GrantType SITE_HOST = "localhost"SITE_PORT = "8000" SITE_URL = f"http://{SITE_HOST}:{SITE_PORT}"LOGOUT_REDIRECT_URL = f"http://{SITE_HOST}:{SITE_PORT}/api/auth/logout"KINDE_CALLBACK_URL = f"http://{SITE_HOST}:{SITE_PORT}/api/auth/kinde_callback" GRANT_TYPE = GrantType.AUTHORIZATION_CODE_WITH_PKCETEMPLATES_AUTO_RELOAD = TrueSESSION_TYPE = "filesystem"SESSION_PERMANENT = FalseCreate a new .env file with the following command:
touch .envFrom your Kinde application > Quick start page, copy the following configuration keys and update your .env file with the values:
KINDE_ISSUER_URL = "https://your_kinde_domain.kinde.com"CLIENT_ID = "your_client_id"CLIENT_SECRET = "your_client_secret"Update the app.py file with the following code:
from dotenv import load_dotenvload_dotenv()from typing import Unionfrom fastapi import FastAPI, Depends, HTTPException, status, Request, APIRouterfrom fastapi.responses import RedirectResponsefrom starlette.middleware.sessions import SessionMiddlewarefrom kinde_sdk.configuration import Configurationfrom kinde_sdk.kinde_api_client import KindeApiClient, GrantTypeimport osimport secrets import config # Your configuration file with necessary settings KINDE_ISSUER_URL = os.getenv("KINDE_ISSUER_URL")CLIENT_ID = os.getenv("CLIENT_ID")CLIENT_SECRET = os.getenv("CLIENT_SECRET")# Secret used for session managementSECRET_KEY = secrets.token_hex(32) app = FastAPI()app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY) # Callback endpointrouter = APIRouter() # Initialize Kinde client with configurationconfiguration = Configuration(host=KINDE_ISSUER_URL)kinde_api_client_params = { "configuration": configuration, "domain": KINDE_ISSUER_URL, "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, "grant_type": config.GRANT_TYPE, "callback_url": config.KINDE_CALLBACK_URL,}if config.GRANT_TYPE == GrantType.AUTHORIZATION_CODE_WITH_PKCE: kinde_api_client_params["code_verifier"] = "" # User clients dictionary to store Kinde clients for each useruser_clients = {} # Dependency to get the current user's KindeApiClient instance def get_kinde_client(request: Request) -> KindeApiClient: user_id = request.session.get("user_id") if user_id is None: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") if user_id not in user_clients: # If the client does not exist, create a new instance with parameters user_clients[user_id] = KindeApiClient(**kinde_api_client_params) kinde_client = user_clients[user_id] # Ensure the client is authenticated if not kinde_client.is_authenticated(): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") return kinde_client # Login endpoint @app.get("/api/auth/login")def login(request: Request): kinde_client = KindeApiClient(**kinde_api_client_params) login_url = kinde_client.get_login_url() return RedirectResponse(login_url) # Register endpoint @app.get("/api/auth/register")def register(request: Request): kinde_client = KindeApiClient(**kinde_api_client_params) register_url = kinde_client.get_register_url() return RedirectResponse(register_url) @app.get("/api/auth/kinde_callback")def callback(request: Request): kinde_client = KindeApiClient(**kinde_api_client_params) kinde_client.fetch_token(authorization_response=str(request.url)) user = kinde_client.get_user_details() request.session["user_id"] = user.get("id") user_clients[user.get("id")] = kinde_client return RedirectResponse(app.url_path_for("read_root")) # Logout endpoint @app.get("/api/auth/logout")def logout(request: Request): user_id = request.session.get("user_id") if user_id in user_clients: kinde_client = user_clients[user_id] logout_url = kinde_client.logout( redirect_to=config.LOGOUT_REDIRECT_URL) del user_clients[user_id] request.session.pop("user_id", None) return RedirectResponse(logout_url) raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") @app.get("/")def read_root(kinde_client: KindeApiClient = Depends(get_kinde_client)): # Now this route requires authentication user = kinde_client.get_user_details() return { "info": "Route protected with Kinde", "user_info": user.get("given_name") + " " + user.get("family_name") } @app.get("/items/{item_id}")def read_item(item_id: int, q: Union[str, None] = None, kinde_client: KindeApiClient = Depends(get_kinde_client)): # This route also requires authentication return {"item_id": item_id, "q": q}Let’s walk through what’s happening in the code now that Kinde authentication has been added.
We start by importing the necessary libraries, including those from the Kinde SDK and starlette for session management. Since Kinde doesn’t handle sessions directly, we use Starlette’s session middleware to manage state on the server side.
Each user’s KindeApiClient instance is stored in a dictionary to keep track of their session state. If a client instance doesn’t already exist, we create a new one and use helper functions—like is_authenticated—to access user details and manage protected routes.
If you’re planning to use this setup in production, we recommend storing session data in a shared store such as Redis, Memcached, or a database. This ensures session persistence across multiple app instances. FastAPI doesn’t include built-in support for server-side session storage, so you’ll need to integrate your own solution for distributed environments.
Once your session management is in place, protecting routes is simple. Just add kinde_client: KindeApiClient = Depends(get_kinde_client) as a parameter to any route function that requires authentication. From there, the route is secured without additional configuration.
Run the development server (if not running already) by using the following bash command:
uvicorn app:app --reloadGo to http://localhost:8000, you will see this API response:
{ "detail": "Not authenticated"}Go to http://localhost:8000/docs to see all your application’s API endpoints.
Sign in or register a new user via either of the endpoints:
After a successful authentication, you will be redirected to the root page with your user info.
You have now projected your FastAPI endpoint with Kinde auth.
{ "info": "Route protected with Kinde", "user_info": "{the logged in user's name}"}You’ve now secured your FastAPI routes using Kinde Auth, combining the performance and flexibility of FastAPI with Kinde’s streamlined approach to authentication.
Remember to follow best practices when handling user data, keep your dependencies up to date, and test your integration thoroughly to ensure secure and reliable flows.
If you need help, reach out to us at support@kinde.com. You can also connect with the team and other developers in the Kinde Slack community or Discord. We’re here to support you.