Authentication and Authorization: How I stopped Overengineering It
For a long time, authentication and authorization felt heavier than they needed to be.
Authentication and Authorization: How I stopped Overengineering It

For a long time, authentication and authorization felt heavier than they needed to be.
Every new project started the same way: sessions vs tokens, cookies vs localStorage, custom middleware and so on. Somewhere along the line, auth became this fragile, over-engineered layer that everyone was afraid to touch, well majorly because you hear about servers being hacked and all.
After building multiple production systems; some custom, some Firebase-based, some JWT-only . I realized something simple:
Most apps don’t fail at authentication because the tools are bad. They fail because the application model is unclear.
The Mental Shift That Changed Everything
The biggest shift for me was separating concerns clearly:
Authentication answers who you are. Authorization answers what you’re allowed to do.
That sounds obvious, but in practice, many apps blur the line. Frontend checks roles. Backend trusts flags sent from the client. Logic gets duplicated. Security becomes accidental.
These days, I follow one rule almost religiously:
The frontend authenticates the user. The backend authorizes every request.
Everything else flows naturally from that.
How I Handle Auth on the Frontend (Without Making It a Mess)
On the frontend, I keep things intentionally boring.
Once a user logs in, Firebase gives me an ID token. That token is a JWT, and it represents the user’s authenticated state. I don’t store it in localStorage. I don’t sync it across tabs manually. I let the Firebase SDK do its job.
All I really need is a single global place to know:
- Is the user logged in?
- Who are they?
- Are we still loading?
For React and Next.js, Context API is more than enough.
When the app loads, I listen for auth state changes. If a user exists, I grab the token and keep it in memory. If not, I clear everything. That’s it.
Now any component can ask, “Do we have a user?” without caring how authentication works under the hood.
Client-side route protection becomes a UX concern, not a security one. If the user isn’t logged in, I redirect them. Not because it’s secure, but because it’s polite. (I use Firebase as a example in this post)
Where the Real Security Actually Lives
The backend is where I stop being polite. The assumption is simple: anyone can call this API. So every request must prove who it is.
The JWT becomes the contract.
When a request hits the backend, I expect an Authorization header. I verify the token using Firebase Admin SDK. If it’s valid, I now know who the user is. If it’s not, the request ends immediately.
No token, no access. Invalid token, no access. Once the token is verified, authorization becomes straightforward.
The One “Hack” I Use in Almost Every Project
If I had to summarize my approach in one sentence, it would be this:
Let Firebase handle identity. Let your backend handle access.
The frontend never decides permissions. The backend never trusts the frontend. The JWT is the single source of truth between them.
This setup has worked for dashboards, internal tools, SaaS products, and client projects. It scales surprisingly well, and it avoids most of the foot-guns people run into with authentication.
I hope this helps you, let me know
메타데이터
- post_id
- d14724b942f9
- slug
- authentication-and-authorization-how-i-to-overengineering-it-d14724b942f9
- url
- https://medium.com/@aamadewoyin/authentication-and-authorization-how-i-to-overengineering-it-d14724b942f9
- canonical_url
- https://medium.com/@aamadewoyin/authentication-and-authorization-how-i-to-overengineering-it-d14724b942f9
- author_url
- https://medium.com/@aamadewoyin
- status
- ok
- fetched_at
- 2026-07-15 17:40:18