DEV Community

Shanu
Shanu

Posted on

Learn JWT in a way you never forget

In today's digital world, security is paramount. Have you ever wondered how websites keep you logged in without asking for your password repeatedly? The secret lies in the clever use of access tokens and refresh tokens. Let's explore how these digital keys work to provide a seamless and secure user experience, with detailed code examples.

The Library Analogy

Imagine you're visiting a grand library with many restricted sections. To enter these sections, you need a special pass, much like how you need permissions to access certain parts of a website. Here's where access tokens and refresh tokens come into play:

  • Access Token: Picture the access token as a temporary pass to the library's restricted sections. It's only valid for a short time, ensuring that if it falls into the wrong hands, it won't be useful for long.

  • Refresh Token (Session Token): On the other hand, the refresh token is like your annual library membership card. It lasts much longer and allows you to renew your temporary pass without having to reapply for membership.

How Access Tokens and Refresh Tokens Work

Let's break down the process of how these tokens work in practice:

Step 1: Logging In

When you first log in to a website with your username and password, the server checks your credentials. If everything matches up, it grants you an access token and a refresh token. Here's an example using JavaScript with Node.js and Express:

// Express server setup
const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());

// Simulated user database
const users = [{ username: 'user1', password: 'password123' }];

// Login route
app.post('/login', (req, res) => {
  const { username, password } = req.body;
  const user = users.find(u => u.username === username && u.password === password);
  if (user) {
    // Generate tokens
    const accessToken = jwt.sign({ username: user.username }, 'access_secret', { expiresIn: '15m' });
    const refreshToken = jwt.sign({ username: user.username }, 'refresh_secret', { expiresIn: '7d' });
    res.json({ accessToken, refreshToken });
  } else {
    res.status(401).send('Invalid credentials');
  }
});

app.listen(3000, () => console.log('Server running on port 3000'));
Enter fullscreen mode Exit fullscreen mode

In this code, jwt.sign() is used to create an access token and a refresh token. The access token is valid for 15 minutes, and the refresh token is valid for 7 days.

Step 2: Using the Access Token

With your access token in hand, you can now access restricted sections of the website. Each time you want to access a resource, you present your access token. Here's an example of a protected route:

// Middleware to verify access token
const authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (token == null) return res.sendStatus(401);

  jwt.verify(token, 'access_secret', (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user;
    next();
  });
};

// Protected route
app.get('/profile', authenticateToken, (req, res) => {
  res.json({ profile: `This is the profile of ${req.user.username}` });
});
Enter fullscreen mode Exit fullscreen mode

Here, the authenticateToken middleware function checks if the access token is valid before granting access to the /profile route.

Step 3: Expiration of the Access Token

After a short while, your access token expires. This is a security measure to ensure that even if someone gets hold of your token, they can't use it indefinitely. If the access token is expired, you'll receive an error:

app.get('/profile', authenticateToken, (req, res) => {
  res.json({ profile: `This is the profile of ${req.user.username}` });
});
Enter fullscreen mode Exit fullscreen mode

If the token is expired, the jwt.verify() function will return an error, and the user will be unable to access the resource.

Step 4: Renewing the Access Token

Instead of logging in again, you use your refresh token to get a new access token. This is like showing your membership card to get a new temporary pass. Here's how you can implement this:

// Route to refresh tokens
app.post('/refresh', (req, res) => {
  const { refreshToken } = req.body;
  if (refreshToken == null) return res.sendStatus(401);

  jwt.verify(refreshToken, 'refresh_secret', (err, user) => {
    if (err) return res.sendStatus(403);
    const accessToken = jwt.sign({ username: user.username }, 'access_secret', { expiresIn: '15m' });
    res.json({ accessToken });
  });
});
Enter fullscreen mode Exit fullscreen mode

In this route, the server verifies the refresh token and issues a new access token if the refresh token is valid.

Step 5: Continuing with the New Access Token

With the new access token, you can continue accessing the restricted sections without a hitch. Simply use the new token in your requests:

// Example of using the new access token
fetch('/profile', {
  headers: {
    'Authorization': `Bearer ${newAccessToken}`
  }
})
.then(response => response.json())
.then(data => console.log(data));
Enter fullscreen mode Exit fullscreen mode

This ensures that the user can continue their session seamlessly after refreshing their access token.

Benefits and Security Considerations

The use of access tokens and refresh tokens offers several advantages:

  1. Enhanced Security: Short-lived access tokens minimize the risk if they're intercepted.
  2. Improved User Experience: Users don't need to log in frequently.
  3. Scalability: Tokens can be validated without hitting the database, reducing server load.
  4. Fine-grained Access Control: Tokens can carry specific permissions, allowing for precise access management.

However, it's crucial to implement proper security measures:

  • Encrypt tokens in transit and at rest
  • Implement token revocation mechanisms
  • Use secure token storage on the client-side
  • Regularly rotate refresh tokens

Real-World Applications

This token-based authentication is widely used in various scenarios:

  1. Single Sign-On (SSO) Systems: Allow users to access multiple applications with one set of credentials.
  2. Mobile Applications: Provide seamless access across app restarts.
  3. API Authentication: Secure communication between different services or microservices.
  4. OAuth 2.0 and OpenID Connect: Popular protocols that leverage token-based authentication for third-party access and identity verification.

Conclusion

By using access tokens and refresh tokens, websites maintain a balance between convenience and security. The short lifespan of access tokens minimizes the risk if they are compromised, while refresh tokens allow users to stay logged in comfortably.

Understanding these concepts not only satisfies curiosity but also helps in appreciating the intricate systems that keep our online experiences smooth and secure. As we continue to rely more on digital services, the importance of robust, user-friendly authentication methods like these will only grow.

For a deeper dive into understanding access and refresh tokens, you might find resources like technical documentation, online courses, or video tutorials helpful. Many developers and security experts share their knowledge on platforms like YouTube, offering clear and straightforward explanations of these complex topics.

Top comments (0)