How to work sqladmin token and secret_key management?

Summary

A typical pitfall when integrating SQLAdmin (or any admin panel) with FastAPI is misusing the secret_key and a custom token within session management. This postmortem analyzes a common user implementation where the token in the session is set to a static literal string ("secret"), and the secret_key is often left unmanaged or hard-coded.

The core issue is that SQLAdmin does not manage authentication tokens; it relies on standard FastAPI SessionMiddleware. The developer is confusing the concept of a session cookie (signed by secret_key) with an application-level authentication token.

Key Takeaway:

  • secret_key is used by the server to sign and verify the integrity of the session cookie (stored in the browser). It must be a long, random string.
  • token (in this context) is simply a value stored inside the session data. It does not grant access unless explicitly checked. Storing a static value like "secret" offers zero security.

Root Cause

The root cause of the confusion and potential security vulnerability lies in two distinct misunderstandings:

  1. Misuse of secret_key: The secret_key is a configuration parameter for the middleware (e.g., SessionMiddleware), not for the SQLAdmin AuthenticationBackend class directly in the way the snippet implies. If this key is hard-coded or weak, an attacker can forge session cookies.
  2. Static Session Token: The implementation sets request.session.update({"token": "secret"}). This stores a static string in the session. The authenticate method merely checks if this key exists. This bypasses proper credential validation on every request and relies on the session cookie’s signature alone. If the secret_key is compromised, an attacker can generate valid cookies with the “token” key present.

Why this happens in Real Systems

  • Documentation Misinterpretation: Users often copy code snippets from tutorials where secret_key is a placeholder, failing to understand it is a cryptographic secret.
  • Ambiguous Variable Naming: Using “token” as a session key implies a JWT or API token, but in this context, it is just a boolean flag stored in the session.
  • Lack of Native Integration: SQLAdmin handles the UI but delegates authentication to the host application (FastAPI). This requires the developer to implement standard session management correctly.

Real-World Impact

If secret_key is hard-coded or weak, and the token logic is flawed, the impacts include:

  • Session Fixation/Hijacking: Without a strong, unique secret_key, an attacker can predict or forge session cookies, allowing them to log in as any user.
  • Authentication Bypass: If the token is static, and the session cookie is intercepted (e.g., via Man-in-the-Middle on HTTP), the attacker has full admin access without knowing the password.
  • Operational Insecurity: Hard-coding secrets violates the principle of least privilege and makes rotating keys impossible without code changes.
  • Confusion and Bugs: Future developers may assume “token” represents a JWT or OAuth token, leading to incorrect logic when extending the auth system.

Example or Code

The following demonstrates the correct way to handle authentication with SQLAdmin. Note the use of secrets for the key and proper validation logic.

Requirements:

from fastapi import FastAPI, Request
from starlette.middleware.sessions import SessionMiddleware
from sqladmin import Admin, AuthenticationBackend
from sqlalchemy.orm import Session

# 1. Generate a secure secret key (run once and store in env variable)
# import secrets; print(secrets.token_hex(32))
SECRET_KEY = "YOUR_GENERATED_LONG_RANDOM_STRING_HERE"

app = FastAPI()

# 2. Add SessionMiddleware with the secret key
app.add_middleware(SessionMiddleware, secret_key=SECRET_KEY)

class AdminAuth(AuthenticationBackend):
    async def login(self, request: Request) -> bool:
        form = await request.form()
        username, password = form["username"], form["password"]

        # Validate credentials against DB
        session = SessionLocal()
        user = session.query(User_model).filter(User_model.username == username).first()

        if user and user.check_password(password): # Assume password hashing is used
            if user.is_admin:
                # Store user ID or relevant info, NOT a static token
                request.session["user"] = {"id": user.id, "username": user.username}
                return True
        return False

    async def logout(self, request: Request):
        request.session.clear()

    async def authenticate(self, request: Request) -> bool:
        # Check if user exists in session
        return request.session.get("user") is not None

authentication_backend = AdminAuth(secret_key=SECRET_KEY)
admin = Admin(app, engine, authentication_backend=authentication_backend)

How Senior Engineers Fix It

Senior engineers approach this by separating concerns and using standard security practices:

  1. Use Environment Variables for Secrets: The secret_key must never be in source control. It is loaded via os.environ.get("SECRET_KEY") or a secrets manager (AWS Secrets Manager, Vault).
  2. Store User Identity, Not Flags: Instead of {"token": "secret"}, store the authenticated user’s ID or object in the session: request.session["user_id"] = user.id. This ensures the session is tied to a specific user.
  3. Implement Stateful Validation: The authenticate method should verify the existence and validity of the user data in the session against the database on every request (or cache it efficiently) rather than just checking for a key’s existence.
  4. Enforce Password Hashing: The example code compares plaintext passwords (password == user.password). Seniors use bcrypt or argon2 to hash input and compare against the stored hash.
  5. HTTPS Enforcement: Ensure SessionMiddleware is configured to only send cookies over HTTPS (https_only=True) to prevent session theft.

Why Juniors Miss It

Junior developers often miss these concepts due to a lack of experience with web security fundamentals:

  • Conceptual Gap on Middleware: They may not understand that SessionMiddleware handles the cryptographic signing of cookies. They treat secret_key as just another config value rather than a cryptographic secret.
  • Over-reliance on “It Works”: If the login redirects successfully, they assume the security is valid, overlooking that the “token” is static and easily replicable.
  • Copy-Paste Programming: Tutorials often use placeholder secrets (e.g., secret_key='super-secret'). Juniors replicate this in production without understanding the risk of hardcoded keys.
  • Confusing Session vs. Token Auth: They blend concepts of Token-based authentication (JWT) with Session-based authentication. In the provided code, using a “token” inside a session is redundant; the session ID is the token.