Chapter 8: Handling Authentication and Authorization
Understand how to implement secure user authentication and authorization flows in SPAs, including token management.
In this chapter, we’ll cover how to securely implement user authentication and authorization in Single Page Applications (SPAs). By the end of this chapter, you’ll understand core authentication concepts, including handling tokens, managing user sessions, and securing access to protected resources.
Authentication vs. Authorization
Authentication verifies a user’s identity (e.g., logging in), while authorization determines what the authenticated user can access. SPAs typically use authentication to verify users and then use authorization to control access to specific features or data.
Implementing Authentication with JWT
JSON Web Tokens (JWTs) are a popular solution for SPA authentication. After logging in, the server issues a JWT that the client stores and sends with each request to verify the user’s identity. Here’s a simple login flow using JWT:
fetch("https://api.example.com/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username: "user", password: "password" })
})
.then(response => response.json())
.then(data => {
localStorage.setItem("token", data.token); // Save JWT to localStorage
})
.catch(error => console.error("Login failed:", error));
In this example, after successful login, the JWT is stored in localStorage
for use in subsequent requests.
Storing and Using Tokens
Tokens can be stored in localStorage
or sessionStorage
. However, for better security, consider storing tokens in an HTTP-only cookie to prevent access by JavaScript and reduce the risk of XSS attacks. Here’s how you might use the token in subsequent API requests:
fetch("https://api.example.com/protected", {
method: "GET",
headers: {
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
.then(data => console.log("Protected data:", data))
.catch(error => console.error("Access denied:", error));
The token is added to the Authorization
header for accessing protected endpoints.
Refreshing Tokens
Tokens have an expiration time. To maintain a user session, SPAs often use refresh tokens, which allow the app to request a new access token when the current one expires. A refresh token is typically stored securely (e.g., in an HTTP-only cookie) and used to obtain a new JWT without requiring the user to log in again.
// Example of refreshing an expired token
fetch("https://api.example.com/refresh-token", {
method: "POST",
credentials: "include"
})
.then(response => response.json())
.then(data => {
localStorage.setItem("token", data.newToken);
})
.catch(error => console.error("Token refresh failed:", error));
This example uses a refresh token to obtain a new JWT and update it in localStorage
.
Protecting Routes in SPAs
To secure certain parts of an SPA, you can implement route guards, which restrict access to routes based on the user’s authentication status. In frameworks like React, route guards are often created with conditional rendering:
function PrivateRoute({ children }) {
const token = localStorage.getItem("token");
return token ? children : ;
}
In this example, the component renders its children only if a valid token exists, otherwise redirecting the user to the login page.
Best Practices for Authentication and Authorization in SPAs
- Use HTTPS: Always serve your SPA over HTTPS to prevent interception of sensitive data.
- Secure Token Storage: Use HTTP-only cookies for storing tokens if possible, and avoid storing sensitive data in
localStorage
. - Implement Token Expiration and Refresh: Ensure tokens have an expiration time, and use refresh tokens to maintain sessions securely.
- Limit Access with Role-Based Authorization: Implement roles and permissions to control access to different parts of your app based on user roles.
Summary and Next Steps
In this chapter, we covered authentication and authorization concepts for SPAs, including implementing JWT authentication, handling tokens, and protecting routes. In the next chapter, we’ll discuss performance optimization techniques to ensure your SPA runs efficiently and smoothly for users.