Building Aegis2FA: Why I Built My Own 2FA Service
I wanted to actually understand authentication, not just copy-paste from tutorials. So I built a 2FA service from scratch.
I got tired of not really understanding how authentication works.
Every tutorial I read showed me how to use Auth0 or Firebase Auth, but never why things worked the way they did. What actually happens when you scan that QR code? Why do some sites use 6-digit codes while others use 8? How does your phone know the right code without talking to the server?
So I decided to build my own 2FA service. Not to replace Auth0 - just to learn.
What I Built
Aegis2FA is a complete two-factor authentication service with:
- TOTP (those 6-digit codes from Google Authenticator)
- Email and SMS verification
- Backup codes for when you lose your phone
- JWT tokens with refresh rotation
It's not production software. It's a learning project with tests and documentation so I can reference it later.
The Parts That Were Harder Than Expected
TOTP clock drift - I assumed phones and servers would have the same time. They don't. Some phones are off by 30+ seconds, which means valid codes get rejected. The fix is simple (accept codes from adjacent time periods), but I didn't think about it until users complained.
Token reuse - TOTP codes are valid for 30 seconds. What stops someone from using the same code twice within that window? Nothing, unless you track used tokens. I missed this initially.
Backup codes - Generating them is easy. The hard part is storing them securely (hashed, not plaintext) while still being able to show the user which codes they've used.
What I Actually Learned
The biggest lesson: authentication is mostly about handling edge cases. The happy path is straightforward. It's the weird situations that get you:
- User's phone clock is wrong
- User loses their device
- Tokens get intercepted
- Someone tries the same code twice
I also learned that Argon2 is better than bcrypt for password hashing (more memory-hard, harder to crack with GPUs), and that JWT refresh token rotation prevents token theft better than long-lived tokens.
Now What?
I'm using these patterns at work now. When I see authentication code, I actually understand what it's doing and why. That was the whole point.
The code is on GitHub if you want to look through it.