← Back to Articles

Modern Authentication Patterns: JWT, OAuth, and Beyond

6 min read

Authentication is one of those things that seems simple until you actually have to implement it. You need to verify that users are who they say they are, and you need to do it securely. But there are so many different approaches, and each one has trade-offs.

I've implemented authentication in a lot of different ways over the years. I've used sessions, JWT tokens, OAuth, and various combinations. Each approach has its place, and understanding when to use which one is important.

Sessions: The traditional approach

Sessions are probably the most straightforward authentication method. When a user logs in, the server creates a session and stores it. The server gives the client a session ID, usually in a cookie, and the client sends that ID with every request. The server looks up the session to see who the user is.

The nice thing about sessions is that they're simple to understand and implement. The server has full control—it can invalidate sessions, see who's logged in, and manage everything centrally.

But sessions have downsides. They require server-side storage, which can be a problem if you have multiple servers. You need to share session storage across all your servers, which usually means using Redis or a database. This adds complexity and can become a bottleneck.

JWT tokens: Stateless and scalable

JWT, or JSON Web Tokens, solve the session storage problem by making authentication stateless. Instead of storing sessions on the server, you encode user information directly into the token. The client sends the token with each request, and the server validates it without needing to look anything up.

This is great for scalability. You can have as many servers as you want, and they don't need to share any state. Each server can validate tokens independently. This makes JWT popular for microservices and distributed systems.

But JWTs have their own challenges. Once you issue a token, you can't easily revoke it until it expires. If someone steals a token, they can use it until it expires. You can work around this with token blacklists, but that brings back the need for shared storage.

I usually use short-lived access tokens—maybe 15 minutes to an hour—and longer-lived refresh tokens. When the access token expires, the client uses the refresh token to get a new one. This limits the damage if a token is stolen.

OAuth: Let someone else handle it

OAuth is great when you want users to log in with accounts they already have, like Google or GitHub. Instead of managing usernames and passwords yourself, you let another service handle authentication. The user authorizes your application, and you get back a token you can use to access their information.

This is convenient for users—they don't need to create another account and remember another password. It's also convenient for you—you don't need to handle password storage, password resets, or any of that complexity.

OAuth can be confusing at first because there are different flows for different use cases. OAuth 2.0 is the current standard, and it defines several grant types. The authorization code flow is the most common for web applications. The user is redirected to the OAuth provider, they log in and authorize your app, and then they're redirected back with a code you exchange for a token.

Passwordless authentication

Passwordless authentication is becoming more popular. Instead of passwords, you use things like magic links sent via email, or one-time codes sent via SMS. The user requests access, gets a code or link, and uses that to log in.

This is great from a security perspective because there are no passwords to steal. Even if someone intercepts a magic link, it's usually time-limited and single-use. But it requires users to have access to their email or phone, which isn't always convenient.

I've implemented magic link authentication, and users seem to like it. It's one less password to remember. But you need to make sure your email delivery is reliable. If the magic link email doesn't arrive, users can't log in.

Multi-factor authentication

For applications that need extra security, multi-factor authentication adds an additional layer. After the user enters their password, they also need to provide something else—usually a code from an authenticator app or sent via SMS.

MFA significantly improves security. Even if someone steals a password, they can't log in without also having access to the user's phone or authenticator app. I always recommend MFA for applications that handle sensitive data.

The challenge is making it user-friendly. Some users find the extra step annoying. But the security benefits are worth it, especially for admin accounts or accounts with elevated privileges.

Choosing the right approach

There's no one-size-fits-all solution for authentication. The right choice depends on your use case. For a simple internal tool, sessions might be fine. For a public API, JWT tokens make more sense. For a consumer application, OAuth or passwordless might be better.

I often use a combination. For my own applications, I might use JWT tokens for API authentication, but also support OAuth for users who want to log in with Google or GitHub. This gives users options while keeping things secure.

Security considerations

No matter which authentication method you choose, there are some security basics you should always follow. Always use HTTPS. Never send credentials or tokens over unencrypted connections.

Hash passwords properly if you're storing them. Use a strong hashing algorithm like bcrypt or Argon2. Never store passwords in plain text, and never use weak hashing algorithms like MD5 or SHA-1.

Implement rate limiting on login endpoints. This prevents brute force attacks where someone tries thousands of password combinations. After a few failed attempts, slow down or block further attempts from that IP.

Token storage

If you're using JWT tokens, you need to think about where to store them on the client. The two main options are cookies and localStorage. Each has trade-offs.

Cookies are more secure because they can be marked as HttpOnly, which means JavaScript can't access them. This protects against XSS attacks. But cookies are sent with every request, which can be inefficient if your tokens are large.

localStorage is easier to work with from JavaScript, but it's vulnerable to XSS attacks. If your application has an XSS vulnerability, an attacker could steal tokens from localStorage.

I usually use HttpOnly cookies for tokens when possible. If I need to access tokens from JavaScript, I might use a different approach or accept the security trade-off.

The bottom line

Authentication is complex, but it doesn't have to be overwhelming. Start with the basics: choose an approach that fits your needs, implement it correctly, and follow security best practices. You can always add more sophisticated features later.

The most important thing is to understand the trade-offs of each approach. There's no perfect solution—each one has pros and cons. Pick the one that makes sense for your application, and implement it well. Good authentication is invisible to users but protects them and your application.

About the author

Rafael De Paz

Full Stack Developer

Passionate full-stack developer specializing in building high-quality web applications and responsive sites. Expert in robust data handling, leveraging modern frameworks, cloud technologies, and AI tools to deliver scalable, high-performance solutions that drive user engagement and business growth. I harness AI technologies to accelerate development, testing, and debugging workflows.

Tags:

Share: