Build Secure Authentication: The Right Way to Handle Tokens | Chandrashekhar Kachawa | Tech Blog

Build Secure Authentication: The Right Way to Handle Tokens

backend

Alright, settle in. I’ve been doing this for a while, and if there’s one thing I’ve learned, it’s that a lot of folks get the easy part right and the crucial part wrong. You’ve heard about JWTs, they’re everywhere, but the real trick isn’t using them—it’s managing them. So let’s talk about how to handle your tokens like a professional, not a panicked amateur.

Know Your Tokens: They’re Not All the Same

This is the first lesson, and it’s a simple one. You’ve got two main players in this game, and their roles couldn’t be more different.

The Access Token:

This is the workhorse. Its job is to get you into the party. It’s short-lived for a reason—think of it as a temporary guest pass. If it gets misplaced, the pass expires before a thief can do much with it. You keep this one in memory, not in some publicly accessible place, because it’s only for the current moment.

The Refresh Token:

This is your long-term relationship. Its only job is to get you a new Access Token when your current one expires. To protect this trusted partner, we keep it safe and sound in a secure HTTP-only cookie. That way, no rogue scripts on the client-side can even touch it.

Backend Basics: The Flow of Control

Your server is the manager of this whole operation. It has two main tasks.

User Login (/api/auth/login)

When a user logs in, your server validates their credentials. If everything checks out, it issues a pair of tokens: a new Access Token and a new Refresh Token. The Access Token goes back in the JSON response, and the Refresh Token is set in that special cookie.

Protected Routes Middleware

For every sensitive endpoint, your server sets up a simple checkpoint. It looks for a valid Access Token in the request header. If the token is valid, the request proceeds. If not, the server politely, but firmly, says “Not today” with a 401 Unauthorized status.

The Single-Use Trick: Preventing Token Theft

This is where we add a clever little layer of security. We treat our Refresh Tokens as one-time-use items. Once a Refresh Token has been used to get a new Access Token, it can never be used again.

Updated Backend Refresh Endpoint (/api/auth/refresh)

This is your most important security gate.

  • A request for a new Access Token arrives with the Refresh Token in the cookie.
  • Your server takes a look at the token’s unique ID (jti) and quickly checks a database. “Has this token been used already?” it asks.
  • If the answer is YES: Oh, dear. This isn’t good. This means someone has tried to use a token that was already exchanged. You immediately revoke all sessions for that user and return a 401 Unauthorized. It’s a clear sign of a malicious attempt.
  • If the answer is NO: Everything is fine. You issue a brand new Access Token and Refresh Token, and then you update your database to mark the old token’s ID as used. Simple, effective, and secure.

A Quick Chat About localStorage

I hear this one a lot: “But why not just save the Access Token in localStorage? It’s so convenient!” And my response is always the same: it’s convenient for an attacker, too.

  • Any JavaScript that gets executed on your page—whether it’s yours or a malicious script from a Cross-Site Scripting (XSS) attack—can read everything in localStorage.
  • If an attacker can inject a single line of code, your Access Token is theirs.
  • By keeping the token in memory, it’s only there while the user is actively on the page. A refresh, a close, or a navigation change, and it’s gone. This simple practice drastically reduces the window of opportunity for an attacker.

The Usual Suspects: Vulnerabilities and Defenses

  • Man-in-the-Middle (MITM) Attacks: A snooper sits between the client and server, listening in.
    • Solution: Use HTTPS. It encrypts all communication, so your data is just a jumble of nonsense to a third party.
  • Cross-Site Scripting (XSS): A malicious script is injected into your page.
    • Solution: Don’t put your sensitive tokens in localStorage. The in-memory Access Token and HTTP-only Refresh Token are your best defenses.
  • Cross-Site Request Forgery (CSRF): An attacker tricks a user into making a secret request from a different website.
    • Solution: Use a CSRF token for your refresh endpoint. This is a special secret your client must send with the refresh request, a secret the attacker can’t possibly know.
  • Replay Attacks: A stolen token is “replayed” over and over to impersonate the user.
    • Solution: The short lifespan of the Access Token and the one-time-use rule for the Refresh Token effectively neuter this attack.

In Conclusion

At the end of the day, building a secure authentication system is about more than just the technology; it’s about the strategy. By understanding the distinct roles of Access and Refresh Tokens, implementing a robust refresh token rotation scheme, and following simple best practices like using an HTTP-only cookie and in-memory storage, you can build a system that is both secure and provides a great user experience. It’s about being thoughtful and proactive, not just reactive.

Good luck out there.

Latest Posts

Enjoyed this article? Follow me on X for more content and updates!

Follow @Ctrixdev