Shipping Auth: Why I Built It Myself with FastAPI

The platform now has a working authentication system. Users can register, log in, and get a personal dashboard tied to their account. Here’s what I built and why.


What I shipped

A FastAPI service that handles:

  • POST /auth/register — creates a user record with a hashed password
  • POST /auth/login — validates credentials, returns a signed JWT
  • Token validation middleware used by all protected endpoints
  • A session state pattern in Streamlit that stores the JWT and passes it on every API call

The Streamlit app calls the FastAPI service on login. If the token is valid, you see the app. If not, you see the login screen. Simple.


Why not just use Supabase Auth?

I considered it. Supabase Auth is genuinely good — it handles email verification, OAuth providers, session management, and it has a Postgres backend you can query directly.

The reason I didn’t use it: I want to own the full stack. Every external service I depend on is a constraint on how I can deploy, how I can price, and how I can eventually move. Since I’m planning to eventually self-host everything on a VM in a specific jurisdiction (partly for latency reasons when I start pulling African market data), I didn’t want authentication to be a cloud dependency I couldn’t replicate.

Rolling my own also meant I understood every part of it. There are no hidden session tables, no magic token refresh flows I don’t control.


What it cost me

About four days I didn’t expect to spend. JWT libraries in Python are fine but the Streamlit session state pattern required some care — Streamlit reruns the entire script on every interaction, so you have to be deliberate about where the token lives and when to check it.

I also had to handle the case where Streamlit and FastAPI are running as separate Docker containers. The API calls from Streamlit needed to hit the FastAPI service by its Docker Compose service name (http://api:8000), not localhost. That caught me for longer than I’d like to admit.


What’s next

The auth system is stable. Next up: finishing the Portfolio Analytics tab in Streamlit. The transaction ledger works, the dbt models are computing portfolio performance, I just need to wire up the charts.