Last active
August 5, 2025 21:59
-
-
Save Darkflib/7763ed90366a2724d6f8468144b9b19b to your computer and use it in GitHub Desktop.
Revisions
-
Darkflib created this gist
Aug 5, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,550 @@ Below is a **step‑by‑step guide** for using **Firebase Authentication** from Python. I’ll cover two typical scenarios: | Scenario | What you’ll do in Python | Which library you’ll use | |----------|--------------------------|--------------------------| | **Client‑side (desktop / script)** – sign‑up, sign‑in, password reset, etc. | Use the Firebase **REST API** (wrapped in the community library **Pyrebase**). | `pyrebase` (or `pyrebase4`) | | **Server‑side (backend, admin tasks)** – verify ID tokens, create custom tokens, manage users. | Use the **Firebase Admin SDK** (official). | `firebase-admin` | Both can coexist in the same project – the client library talks to Firebase as a regular user, the admin SDK talks as a privileged service account. --- ## 1️⃣ Prerequisites | Item | Why you need it | How to get it | |------|----------------|---------------| | **Firebase project** | All auth calls are scoped to a project. | Create one at https://console.firebase.google.com | | **Enable auth providers** (Email/Password, Google, etc.) | Determines what users can use to sign in. | In **Authentication → Sign‑in method**. | | **Service‑account JSON** (for admin SDK) | Gives your Python backend privileged access. | **Project Settings → Service accounts → Generate new private key**. | | **Python ≥ 3.8** | Modern syntax, `asyncio` support, etc. | Install from python.org or your package manager. | | **Virtual environment** (optional but recommended) | Keeps dependencies isolated. | `python -m venv venv && source venv/bin/activate` | --- ## 2️⃣ Installing the Libraries ```bash # (inside your virtual env) pip install pyrebase4 # community wrapper around the Firebase REST API pip install firebase-admin # official admin SDK pip install Flask # optional – for a web backend demo ``` > **Tip:** `pyrebase4` is a maintained fork of the original `pyrebase`. If you hit a bug, try the latest fork. --- ## 3️⃣ Client‑Side Auth with *Pyrebase* ### 3.1 Initialize Pyrebase Create a **`firebase_config.py`** (or load from env vars – never commit secrets). ```python # firebase_config.py import os import json # You can copy the config object from the Firebase console → Project Settings → General → "Your apps" FIREBASE_CONFIG = { "apiKey": os.getenv("FIREBASE_API_KEY"), "authDomain": os.getenv("FIREBASE_AUTH_DOMAIN"), "databaseURL": os.getenv("FIREBASE_DATABASE_URL"), "projectId": os.getenv("FIREBASE_PROJECT_ID"), "storageBucket": os.getenv("FIREBASE_STORAGE_BUCKET"), "messagingSenderId": os.getenv("FIREBASE_MESSAGING_SENDER_ID"), "appId": os.getenv("FIREBASE_APP_ID") } ``` ```python # client.py import pyrebase from firebase_config import FIREBASE_CONFIG firebase = pyrebase.initialize_app(FIREBASE_CONFIG) auth = firebase.auth() ``` ### 3.2 Email/Password Sign‑up & Sign‑in ```python def signup(email: str, password: str): try: user = auth.create_user_with_email_and_password(email, password) print("✅ User created:", user["localId"]) # Optionally send a verification email auth.send_email_verification(user['idToken']) return user except Exception as e: # Pyrebase throws a generic Exception; unpack the JSON for details. err = json.loads(e.args[1]) print("❌ Sign‑up error:", err["error"]["message"]) return None def signin(email: str, password: str): try: user = auth.sign_in_with_email_and_password(email, password) # `user` dict has: # idToken (JWT for the user, valid ~1h) # refreshToken (to get a new idToken) # localId (UID) print("✅ Signed in! UID:", user["localId"]) return user except Exception as e: err = json.loads(e.args[1]) print("❌ Sign‑in error:", err["error"]["message"]) return None ``` ### 3.3 Password Reset & Email Verification ```python def send_password_reset(email: str): try: auth.send_password_reset_email(email) print("✅ Password‑reset email sent") except Exception as e: err = json.loads(e.args[1]) print("❌ Reset error:", err["error"]["message"]) def resend_verification(email: str, password: str): # Must sign‑in to get a fresh idToken, then call send_email_verification user = signin(email, password) if user: auth.send_email_verification(user['idToken']) print("✅ Verification email re‑sent") ``` ### 3.4 Refreshing the ID Token ID tokens expire after ~1 hour. Use the *refresh token* to get a fresh one without prompting the user again: ```python def refresh_id_token(refresh_token: str): try: refreshed = auth.refresh(refresh_token) # refreshed["idToken"] is the new JWT # refreshed["userId"] is the UID return refreshed except Exception as e: print("❌ Refresh error:", e) return None ``` ### 3.5 Using the Token to Call a Protected API When you have an `idToken`, pass it in an `Authorization: Bearer <token>` header to your backend: ```python import requests def call_protected_endpoint(user): id_token = user["idToken"] headers = {"Authorization": f"Bearer {id_token}"} resp = requests.get("https://my‑api.example.com/profile", headers=headers) print("Server response:", resp.json()) ``` --- ## 4️⃣ Server‑Side Auth with *firebase‑admin* The Admin SDK lets you **verify** the ID token sent by the client, **create** users/custom tokens, **manage** accounts, and **set custom claims** (roles). ### 4.1 Initialize the Admin SDK ```python # admin_init.py import firebase_admin from firebase_admin import credentials, auth # Option 1: set env var GOOGLE_APPLICATION_CREDENTIALS="/path/to/key.json" # Option 2: load explicitly (preferred for testability) cred = credentials.Certificate("path/to/serviceAccountKey.json") firebase_admin.initialize_app(cred) ``` > **Important:** *Never* ship the service‑account key with a client‑side app. Keep it on a server‑side environment only. ### 4.2 Verifying an ID Token (Flask Example) ```python # server.py from flask import Flask, request, jsonify import firebase_admin from firebase_admin import auth as admin_auth from admin_init import * # ensures firebase_admin is initialized app = Flask(__name__) def verify_token(id_token): try: decoded_token = admin_auth.verify_id_token(id_token) # `uid` is the Firebase UID return decoded_token except firebase_admin.exceptions.FirebaseError as e: # Token invalid, expired, revoked, etc. print("❌ Token verification failed:", e) return None @app.route("/profile") def profile(): auth_header = request.headers.get("Authorization", "") if not auth_header.startswith("Bearer "): return jsonify({"error": "Missing Bearer token"}), 401 id_token = auth_header.split("Bearer ")[1] decoded = verify_token(id_token) if not decoded: return jsonify({"error": "Invalid token"}), 401 uid = decoded["uid"] # Example: Pull additional data from Firestore/RealtimeDB return jsonify({ "uid": uid, "email": decoded.get("email"), "name": decoded.get("name"), "customClaims": decoded.get("firebase", {}).get("sign_in_attributes") }) if __name__ == "__main__": app.run(debug=True, port=5000) ``` **What’s happening?** 1. Client (Pyrebase) signs in → receives `idToken`. 2. Client includes the token in `Authorization: Bearer …`. 3. Flask endpoint extracts the token and calls `admin_auth.verify_id_token`. 4. If verification succeeds, you get a dict with `uid`, `email`, `name`, and any **custom claims** you set. ### 4.3 Creating a User (Admin) ```python def create_user(email: str, password: str, display_name: str = None): try: user = admin_auth.create_user( email=email, password=password, display_name=display_name, email_verified=False, ) print("✅ Created user:", user.uid) return user except firebase_admin.exceptions.FirebaseError as e: print("❌ Could not create user:", e) return None ``` > **Use case:** When you need to provision accounts programmatically (e.g., admin portal, migration from legacy DB). ### 4.4 Generating a Custom Token (for OAuth‑like flows) Suppose you have your own identity provider (e.g., LDAP) and want to let users sign into Firebase using that credential. The server generates a **custom token**, which the client then exchanges for an `idToken` via the client SDK. ```python def generate_custom_token(uid: str, additional_claims: dict = None) -> str: """ Args: uid: The UID you want the user to have in Firebase. additional_claims: Optional dict with custom claims (e.g., roles). Returns: A signed JWT (custom token) that can be handed to the client. """ try: token = admin_auth.create_custom_token(uid, additional_claims) # `token` is a bytes object; decode for transport return token.decode('utf-8') except firebase_admin.exceptions.FirebaseError as e: print("❌ Custom token error:", e) raise ``` **Client side (Pyrebase) to exchange the custom token:** ```python def sign_in_with_custom_token(custom_token: str): # Pyrebase has a thin wrapper called sign_in_with_custom_token user = auth.sign_in_with_custom_token(custom_token) print("✅ Signed in with custom token, UID:", user["localId"]) return user ``` ### 4.5 Setting & Verifying Custom Claims (Roles) ```python def set_user_role(uid: str, role: str): # Role can be anything: 'admin', 'editor', 'beta_tester', … admin_auth.set_custom_user_claims(uid, {"role": role}) print(f"✅ Set role={role} for uid={uid}") def get_user_role(uid: str) -> str: user = admin_auth.get_user(uid) return user.custom_claims.get('role') if user.custom_claims else None ``` When you later verify an ID token, those claims appear in the decoded token: ```python decoded = admin_auth.verify_id_token(id_token) role = decoded.get('role') # <-- note: top‑level, not under `firebase` ``` Now you can do **RBAC** in your Flask middlewares: ```python def admin_required(f): @wraps(f) def wrapper(*args, **kwargs): token = request.headers.get('Authorization', '').split('Bearer ')[-1] decoded = verify_token(token) if not decoded or decoded.get('role') != 'admin': return jsonify({"error": "admin required"}), 403 return f(*args, **kwargs) return wrapper @app.route("/admin/dashboard") @admin_required def admin_dashboard(): return jsonify({"msg": "Welcome, admin!"}) ``` --- ## 5️⃣ Testing Locally with the **Firebase Authentication Emulator** Running against production every time is slow and can be risky. Firebase provides an **Authentication Emulator** that behaves exactly like the real service (including email/password, custom tokens, etc.) but does not send real emails. ### 5️⃣ Setup ```bash # Install the emulator suite (requires Node.js) npm install -g firebase-tools # Initialize the emulator config (once) firebase init emulators # When prompted, enable the Auth emulator and choose a port (e.g., 9099) ``` ### 5️⃣ Running ```bash firebase emulators:start --only auth ``` You’ll see console output confirming the Auth emulator is listening (default: `http://localhost:9099`). ### 5️⃣ Pointing Pyrebase & Admin SDK at the Emulator **Pyrebase (client):** Override the `authDomain` & `apiKey` with dummy values— they aren’t used for the emulator, but the SDK still expects them. ```python # In firebase_config.py (development) FIREBASE_CONFIG = { "apiKey": "fake-api-key", "authDomain": "localhost", "databaseURL": "http://localhost:9000", "projectId": "my-emulated-project", # The below are not required for auth, but keep them for completeness "storageBucket": "", "messagingSenderId": "", "appId": "fake-app-id" } ``` **Admin SDK (server):** Set the `FIREBASE_AUTH_EMULATOR_HOST` environment variable. ```bash export FIREBASE_AUTH_EMULATOR_HOST="localhost:9099" ``` Now all admin calls (e.g., `create_user`, `verify_id_token`) hit the emulator instead of production. This is perfect for CI/tests. --- ## 6️⃣ Common Pitfalls & Best Practices | Pitfall | Why it happens | How to avoid / fix | |--------|----------------|--------------------| | **Storing the `idToken` forever** | Tokens expire after ~1 h. Using an expired token yields `401` errors. | Store **both** `idToken` and `refreshToken`. Refresh automatically (`auth.refresh(refresh_token)`) before the token expires (e.g., check remaining time via `jwt.decode(id_token, options={"verify_signature": False})['exp']`). | | **Hard‑coding service‑account JSON** in source control | Accidentally exposing privileged credentials. | Keep the JSON file out of Git (`.gitignore`) and load via environment variable (`GOOGLE_APPLICATION_CREDENTIALS`) or secrets manager (AWS Secrets Manager, GCP Secret Manager). | | **Mixing up Auth vs. Database rules** | Assuming Firestore rules are enough for security. | Use **Firebase Security Rules** for each product (Firestore, Realtime DB, Storage) **and** verify ID tokens on server if you have additional backend logic. | | **Relying on Pyrebase for social sign‑in** | Pyrebase only wraps the email/password flow; Google/Facebook sign‑in need the Web SDK or native SDKs. | For social providers, either (a) perform sign‑in on the client (Web, Android, iOS) and send the resulting `idToken` to your Python backend, **or** (b) generate a custom token on the server after you have verified the external provider’s token. | | **Custom claim size limit (1000 bytes)** | Adding too much data leads to `INVALID_ARGUMENT` errors. | Store only **essential** data (role, tier). For richer profile data, keep it in Firestore and reference it by UID. | | **Not handling token revocation** | Revoked users can still access APIs until the token expires. | Call `admin_auth.revoke_refresh_tokens(uid)` when you want to force logout, and on the backend check the `auth_time` claim vs. `revoked_at` (`admin_auth.get_user(uid).tokens_valid_after_timestamp`). | ### Logging & Monitoring * Use **Google Cloud Logging** (via the `google-cloud-logging` library) for critical auth events (sign‑in, token revocation). * Set up **Firebase Alerts** in the console to be notified about unusual spikes (e.g., many failed sign‑ins). --- ## 7️⃣ Full Minimal Working Example Below is a self‑contained script that: 1. **Creates** a test user (admin SDK). 2. **Signs in** with Pyrebase (client). 3. **Calls** a Flask endpoint with the `idToken`. 4. **Verifies** the token (admin SDK) and returns the UID. ```python # --------------------------------------------------- # config.py – keep secrets out of version control # --------------------------------------------------- import os # Set these in your environment or a .env file (use python-dotenv to load) FIREBASE_API_KEY = os.getenv("FIREBASE_API_KEY") FIREBASE_PROJECT_ID = os.getenv("FIREBASE_PROJECT_ID") SERVICE_ACCOUNT_PATH = os.getenv("GOOGLE_APPLICATION_CREDENTIALS") # path to JSON # --------------------------------------------------- # admin.py – initialize Admin SDK # --------------------------------------------------- import firebase_admin from firebase_admin import credentials, auth as admin_auth cred = credentials.Certificate(SERVICE_ACCOUNT_PATH) firebase_admin.initialize_app(cred) # --------------------------------------------------- # client.py – Pyrebase wrapper # --------------------------------------------------- import pyrebase from config import FIREBASE_API_KEY, FIREBASE_PROJECT_ID pyre_cfg = { "apiKey": FIREBASE_API_KEY, "authDomain": f"{FIREBASE_PROJECT_ID}.firebaseapp.com", "databaseURL": f"https://{FIREBASE_PROJECT_ID}.firebaseio.com", "projectId": FIREBASE_PROJECT_ID, "storageBucket": f"{FIREBASE_PROJECT_ID}.appspot.com", "messagingSenderId": "1234567890", "appId": "1:1234567890:web:abcdef123456" } firebase = pyrebase.initialize_app(pyre_cfg) auth = firebase.auth() # --------------------------------------------------- # server.py – Flask backend # --------------------------------------------------- from flask import Flask, request, jsonify from admin import admin_auth import firebase_admin.exceptions app = Flask(__name__) def verify_id(id_token): try: return admin_auth.verify_id_token(id_token) except firebase_admin.exceptions.FirebaseError as exc: print("⚠️ Token verify error:", exc) return None @app.route("/whoami") def whoami(): header = request.headers.get("Authorization", "") if not header.startswith("Bearer "): return jsonify({"error": "Missing Bearer"}), 401 token = header.split("Bearer ")[1] decoded = verify_id(token) if not decoded: return jsonify({"error": "Invalid token"}), 401 return jsonify({ "uid": decoded["uid"], "email": decoded.get("email"), "claims": decoded.get("role") # example custom claim }) # --------------------------------------------------- # demo.py – Orchestrates everything # --------------------------------------------------- import time from client import auth from admin import admin_auth import requests TEST_EMAIL = "[email protected]" TEST_PASSWORD = "SuperSecret123!" # 1️⃣ Ensure the test user exists try: admin_auth.get_user_by_email(TEST_EMAIL) print("🔎 User already exists") except firebase_admin.exceptions.NotFoundError: admin_auth.create_user(email=TEST_EMAIL, password=TEST_PASSWORD) print("✅ Created test user") # 2️⃣ Sign in with Pyrebase user = auth.sign_in_with_email_and_password(TEST_EMAIL, TEST_PASSWORD) print("🔑 Received idToken (expires in ~1h)") # 3️⃣ Call Flask backend (run Flask in a separate terminal or thread) # For demo we start it in a background thread. import threading def run_flask(): app.run(port=5005, debug=False) flask_thread = threading.Thread(target=run_flask, daemon=True) flask_thread.start() time.sleep(1) # give Flask a moment to start # 4️⃣ Make an authorized request headers = {"Authorization": f"Bearer {user['idToken']}"} resp = requests.get("http://127.0.0.1:5005/whoami", headers=headers) print("✅ Backend response:", resp.json()) ``` **Run it:** ```bash # 1. Set environment variables (or export via .env) export FIREBASE_API_KEY="AIzaSy...." export FIREBASE_PROJECT_ID="my-firebase-project" export GOOGLE_APPLICATION_CREDENTIALS="/path/to/serviceAccountKey.json" # 2. Install dependencies if you haven’t yet pip install pyrebase4 firebase-admin Flask python-dotenv requests # 3. Execute python demo.py ``` You should see the user created (or already existing), a successful sign‑in, and a JSON response from the Flask endpoint that includes the UID and email. --- ## 8️⃣ Going Further | Feature | How to add it in Python | |---------|------------------------| | **Phone‑Number auth** | Use the **Firebase Auth REST API** directly (Pyrebase does not expose it). Look at `POST https://identitytoolkit.googleapis.com/v1/accounts:sendVerificationCode` and then `accounts:signInWithPhoneNumber`. | | **OAuth (Google, Facebook, Apple)** | Perform the OAuth flow on a client (web or mobile) to obtain a Google/Facebook ID token, then call `admin_auth.create_custom_token(uid, ...)` and send that custom token to the Python client for sign‑in. | | **Multi‑factor authentication (MFA)** | Currently only the client SDKs (Web/Android/iOS) have first‑class support. In Python you can still verify the 2FA `idToken` the same way. | | **User import/export** | Use `admin_auth.import_users` and `admin_auth.list_users`. Good for migration from a legacy DB. | | **Email templates customization** | Configure in the console or via the **Firebase Management API** (`projects.accounts.templates`). Not required for simple use cases. | | **Testing with PyTest** | Mock `auth`/`admin_auth` calls with `unittest.mock` or spin up the emulator in CI (`firebase emulators:exec`). | --- ## TL;DR Checklist 1. **Create a Firebase project** → enable required sign‑in providers. 2. **Add a service‑account JSON** → keep it safe, load with `firebase_admin`. 3. **Client side** (`pyrebase`): * `auth.create_user_with_email_and_password` → sign‑up * `auth.sign_in_with_email_and_password` → get `idToken`/`refreshToken` * Refresh token when needed. 4. **Server side** (`firebase-admin`): * `admin_auth.verify_id_token(id_token)` → trust user identity. * `admin_auth.create_custom_token(uid, claims)` → integrate external auth. * `admin_auth.set_custom_user_claims(uid, {"role": "admin"})` → RBAC. 5. **Protect your API** → `Authorization: Bearer <idToken>` + token verification. 6. **Use the Auth Emulator** for local development. 7. **Never ship service‑account keys**; rotate them regularly. You now have everything you need to **authenticate users, protect endpoints, manage accounts, and integrate custom authentication flows** from Python using Firebase. Happy coding! 🚀