Securing API requests with $httpInterceptor

Loading

In modern web applications, securing API requests is crucial for protecting sensitive data from unauthorized access, ensuring data integrity, and preventing malicious activities. In AngularJS, $httpInterceptor is a powerful mechanism that allows developers to intercept and modify HTTP requests and responses globally. This capability is especially useful for implementing features such as adding authentication tokens to requests, handling errors, logging, and more.

In this guide, we’ll explore how to use $httpInterceptor to secure API requests, focusing on common security measures like attaching authentication tokens and handling unauthorized access.

What is $httpInterceptor?

$httpInterceptor is a service in AngularJS that provides a way to globally intercept and modify HTTP requests before they are sent to the server and responses when they are returned from the server. This service is ideal for applying common logic across multiple HTTP requests, such as authentication, logging, or error handling.

The $httpInterceptor service allows us to implement the following:

  • Modify HTTP requests: You can add headers, tokens, or other data to the request before sending it to the server.
  • Modify HTTP responses: You can inspect the response before it reaches the calling code, such as handling errors, logging, or transforming the data.
  • Handle HTTP errors globally: You can catch errors (e.g., authentication failures) and handle them in a consistent manner across the application.

Securing API Requests with $httpInterceptor

When securing API requests, the most common need is to include authentication tokens (like JWT) in the request headers to ensure the user is authorized to access the requested resources. Let’s walk through the steps of securing API requests using $httpInterceptor in AngularJS.

Step 1: Create an $httpInterceptor Service

To create an HTTP interceptor in AngularJS, you need to register a new interceptor using the $httpProvider.interceptors array. Here’s how you can implement an interceptor for attaching a JWT token to every API request:

Example: Attaching JWT to Requests

  1. Create the Interceptor:
angular.module('app')
.factory('authInterceptor', function($q, $window) {
return {
// Intercept outgoing requests
request: function(config) {
const token = $window.localStorage.getItem('authToken'); // Get the token from localStorage

if (token) {
config.headers['Authorization'] = 'Bearer ' + token; // Add the token to request headers
}

return config; // Return the modified config
},

// Intercept response errors
responseError: function(rejection) {
// Handle authorization errors (401)
if (rejection.status === 401) {
console.log('Unauthorized! Redirecting to login...');
// Redirect to the login page (or show a modal, etc.)
window.location.href = '/login';
}
return $q.reject(rejection); // Reject the promise with the error
}
};
});

In this example, the interceptor checks if a JWT token is stored in localStorage and appends it to the Authorization header of each request. Additionally, it handles response errors, such as when the server returns a 401 Unauthorized status, and can redirect the user to the login page.

  1. Register the Interceptor:

To enable the interceptor, you need to register it in your AngularJS app’s configuration:

angular.module('app')
.config(function($httpProvider) {
// Register the authInterceptor to be used with every HTTP request
$httpProvider.interceptors.push('authInterceptor');
});

Now, every time you make an HTTP request, the authInterceptor will automatically add the authentication token (if available) to the request headers.

Step 2: Handle Token Expiry and Refresh

In some applications, JWT tokens have an expiration time (e.g., 1 hour). After this time, the token will become invalid, and subsequent API requests will return a 401 Unauthorized error. To handle this, you can implement token refresh logic in the $httpInterceptor service.

Example: Token Refresh Logic

Here’s how you can modify the responseError method in the interceptor to handle expired tokens by attempting to refresh the token:

angular.module('app')
.factory('authInterceptor', function($q, $window, $http, $rootScope) {
return {
request: function(config) {
const token = $window.localStorage.getItem('authToken'); // Get token from localStorage

if (token) {
config.headers['Authorization'] = 'Bearer ' + token; // Add the token to request headers
}

return config; // Return the modified request configuration
},

responseError: function(rejection) {
if (rejection.status === 401 && rejection.config && !rejection.config.__isRetryRequest) {
// Token has expired or is invalid
const refreshToken = $window.localStorage.getItem('refreshToken');

if (refreshToken) {
// Attempt to refresh the token
const refreshRequest = {
method: 'POST',
url: '/api/refresh-token', // API endpoint to refresh the token
data: { refreshToken: refreshToken }
};

return $http(refreshRequest).then(function(response) {
// Store the new token
$window.localStorage.setItem('authToken', response.data.authToken);

// Retry the original request with the new token
rejection.config.__isRetryRequest = true; // Mark the request as a retry
rejection.config.headers['Authorization'] = 'Bearer ' + response.data.authToken;

return $http(rejection.config); // Retry the original request
}).catch(function() {
// If refreshing the token fails, redirect to login
$rootScope.$broadcast('user:logout');
window.location.href = '/login';
});
} else {
// No refresh token available, redirect to login
$rootScope.$broadcast('user:logout');
window.location.href = '/login';
}
}

return $q.reject(rejection); // Reject the response error
}
};
});

Explanation:

  • If the request fails with a 401 Unauthorized error, the interceptor checks if a refreshToken is available.
  • If the refreshToken exists, the interceptor sends a request to refresh the token.
  • Once the token is refreshed, it retries the original request with the new token.
  • If the refresh token is also expired or invalid, the user is logged out and redirected to the login page.

Step 3: Logging Out Users (Handling Token Expiry)

When the token expires and cannot be refreshed, it is essential to log out the user and redirect them to the login page. This can be done in the responseError method, where the application broadcasts a logout event:

$rootScope.$broadcast('user:logout');

The logout event can be used throughout the application to clear user data, reset application state, and navigate the user to the login page.

Step 4: Testing the Interceptor

After implementing the $httpInterceptor, you can test it by making an API request and checking if the authentication token is attached to the request headers. Additionally, simulate a 401 Unauthorized response to ensure the interceptor handles token expiration and refresh correctly.

Example: Testing a Secure API Request

angular.module('app')
.controller('MyController', function($http) {
$http.get('/api/secure-data')
.then(function(response) {
console.log('Data received:', response.data);
})
.catch(function(error) {
console.log('Error:', error);
});
});

Leave a Reply

Your email address will not be published. Required fields are marked *