Building auth for a multi-tenant PaaS
This time I started building the auth foundation for Uploy…
The main idea across these commits was pretty simple: I wanted users to be able to sign up with email + password first, then I wanted to harden the flow a bit with rate limiting and sliding sessions, and finally add GitHub + Google OAuth2 for social login.
I should admit… I probably over-engineered this part a bit early. I added things like rate limiting and workspace handlers right away, even though the app is still in a relatively early stage. But that instinct came from my previous work in 2025, where I had already built a multi-tenant application before (see the red arrow in my portfolio below), so tenant/workspace concerns were already sitting in my head while I worked on this.

In this commit and this commit, I added the basic auth system using email and password. Users can now register, log in, get a session, and access protected routes.
There were a couple of small decisions here that are worth mentioning.
For password hashing, I used Argon2. My reasoning was honestly pretty simple… OWASP mentions Argon2id and explains that Argon2 was the winner of the Password Hashing Competition in 2015, so that felt like a strong enough signal for me to use it here. If you want the deeper security reasoning, the OWASP page is worth reading.
I also created a workspace automatically during registration by splitting the email before @. So if someone signs up with wahyu@email.com, the first workspace name becomes wahyu. This is obviously not the final design and probably not the best long-term approach, because later I want proper workspace creation as part of onboarding. But for now, it was a simple way to connect auth with the multi-tenant direction of the product.
Outside of that, the rest of the work was the usual auth plumbing: register, login, session creation, MeHandler, middleware, membership, and the other pieces needed to make a multi-tenant PaaS feel more structured.
Here’s the demo video…
After that, in this commit, I added a sliding session model.
Before this, I was setting the session cookie with a fixed MaxAge of 7 days. That means once the browser had stored the cookie for 7 days, the user would have to log in again to get a fresh session, even if they were still actively using the app. So I changed it to a sliding session, where the session lifetime keeps moving forward when there is activity… but not forever.
That trade-off felt more reasonable to me. Active users do not get logged out too aggressively, but the session still has an absolute lifetime so it cannot be renewed indefinitely. I liked that balance, and it also matches the kind of trade-off OWASP talks about when discussing session lifetime decisions.
Finally, in this commit, I added Google and GitHub OAuth2 social login.
At this point, the auth flow started to feel much closer to a real product. Email + password is still there, but now users can also choose a more familiar login path through GitHub or Google, which feels especially natural for a developer-facing product like this.
Here’s the demo video…