How to Add Firebase Authentication to a Python FastAPI App
FastAPI’s speed and modern design make it a favorite for building APIs, but handling user authentication can feel like a separate beast. Luckily, Firebase Authentication offers a ready‑made, scalable solution that pairs nicely with Python. In this guide we’ll walk through the essentials: setting up a Firebase project, wiring the SDK into FastAPI, and protecting endpoints with JWT validation.
Why Pair Firebase with FastAPI?
Firebase takes care of the heavy lifting—email/password sign‑in, social providers, token refresh, and even password resets—while FastAPI handles the business logic. The result is a clean separation: you focus on your domain, Firebase handles identity.
- Scalable out of the box: No need to manage a separate user database.
- Built‑in security: Tokens are signed with Google’s private keys, making forgery extremely unlikely.
- Cross‑platform support: Same auth flow works for web, iOS, Android, or other back‑ends.
Prerequisites
Before diving in, make sure you have:
- Python 3.9+ installed.
- FastAPI and
uvicorn(runpip install fastapi uvicorn). - A Firebase project (you’ll need the
serviceAccountKey.jsonfile). - The
firebase-adminPython package (pip install firebase-admin).
Step 1 – Create and Configure the Firebase Project
Head to the Firebase console and create a new project if you haven’t already. Under “Authentication” enable the sign‑in methods you need—email/password is the simplest start.
Next, generate a service account:
- Project Settings → Service Accounts.
- Click “Generate new private key” and download the JSON file.
- Store this file securely; you’ll reference it in your FastAPI code.
Step 2 – Initialize Firebase Admin SDK in FastAPI
Place the downloaded serviceAccountKey.json somewhere your app can read it (e.g., a config/ folder). Then, add the initialization code:
import firebase_adminfrom firebase_admin import credentials, auth
cred = credentials.Certificate('config/serviceAccountKey.json')
firebase_admin.initialize_app(cred)
It’s usually a good idea to wrap this in a separate module, say auth_firebase.py, so you can import auth wherever you need it.
Step 3 – Verify ID Tokens in a Dependency
FastAPI’s dependency injection system shines for auth. Here’s a compact dependency that extracts the bearer token, verifies it with Firebase, and raises an HTTP 401 if anything’s off:
from fastapi import Depends, Header, HTTPException, statusfrom firebase_admin import auth
async def get_current_user(authorization: str = Header(...)):
if not authorization.startswith('Bearer '):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Invalid authentication scheme.'
)
id_token = authorization.split(' ')[1]
try:
decoded_token = auth.verify_id_token(id_token)
return decoded_token # contains uid, email, etc.
except Exception:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail='Could not validate credentials.'
)
This function can now be used as a Depends argument on any route that requires a logged‑in user.
Step 4 – Protect Your Endpoints
Let’s see the dependency in action. The following route returns the authenticated user's UID and email:
from fastapi import FastAPI, Dependsapp = FastAPI()
@app.get("/me")
async def read_current_user(user: dict = Depends(get_current_user)):
return {"uid": user['uid'], "email": user.get('email')}
Try hitting /me with a valid Firebase ID token in the Authorization header, and you’ll get a JSON payload with the user’s info. If the token is missing or expired, FastAPI automatically responds with a 401.
Step 5 – Refresh Tokens on the Client Side
Firebase issues short‑lived ID tokens (about one hour). The client SDK automatically refreshes them, but if you’re writing a custom client you’ll need to call the Firebase REST endpoint https://securetoken.googleapis.com/v1/token with the refresh token. From the server’s perspective, you don’t need to do anything—just keep verifying whatever token arrives.
Handling Edge Cases
Real‑world apps rarely have a single happy path. Here are a few gotchas you might run into:
- Revoked tokens: Users can be forced to sign out from the Firebase console. Call
auth.check_revoked_id_token()if you need to enforce immediate revocation. - Custom claims: Want to add roles like “admin”? Use
auth.set_custom_user_claims(uid, {'admin': True})and check the claim inget_current_user. - Rate limits: Firebase Admin SDK caches public keys, but extremely high request volumes might still hit limits. Consider adding a thin caching layer around token verification if you anticipate heavy traffic.
Testing Locally
When you run uvicorn main:app --reload, you can test with the Firebase CLI’s auth:emulators:start to spin up a local auth emulator. It mimics the real service, letting you generate test tokens without touching production data.
Deploying to Production
On a cloud platform (e.g., Railway, Render, or AWS Lambda via Mangum), make sure the service account JSON isn’t baked into the image. Instead, store it as an environment variable or secret, write it to a temporary file at startup, and point the SDK to that path. This keeps your credentials out of version control.
Where to Go From Here
Firebase Authentication is just one piece of the puzzle. Once you’ve secured your API, you might want to:
- Integrate Firestore for user‑specific data.
- Use Firebase Cloud Messaging to push notifications based on FastAPI events.
- Add rate limiting or API keys for public endpoints.
All of those extensions still play nicely with the same authentication flow we built here.