AngularJS Interceptors for secure page management

I recently started working with AngularJS and I'm facing an issue with my interceptor that catches 401 errors from server responses.

When a 401 status is detected, it triggers a "loginRequired" message broadcast and redirects to the login page. However, there's a brief moment where the restricted page flashes before the redirection occurs. I'm still learning about asynchronous operations and promises, so I'd appreciate any guidance on what I might be doing wrong.

Here's the simplified version of my interceptor code:

var app = angular.module('app', []);

app.factory('myInterceptor', ['$q', '$rootScope',
  function($q, $rootScope) {

    var myInterceptor = {

      'responseError': function(rejection) {
        $rootScope.$broadcast('event:loginRequired');
        return $q.reject(rejection);
      }
    };

    return myInterceptor;
  }
]);

Injecting my interceptor:

app.config(['$httpProvider', function($httpProvider) {
  $httpProvider.interceptors.push('myInterceptor');
}]);

Restricted page route:

.when('/restrictedPage', {
  templateUrl: 'partials/restrictedPage.html',
  controller: 'RestrictedPageController'
}).

Restricted page controller:

app.controller('RestrictedPageController', function($scope) {

  //Alert message sometimes appears, sometimes not
  alert("Oops, shouldn't be here");

});

$rootScope event listener:

$rootScope.$on('event:loginRequired', function() {

  //Only redirect if we aren't on the free access page
  if ($location.path() == "/freeAccess")
    return;

  //Redirect to the login page otherwise
  $location.path('/home').replace();

});

The flashing issue seems to be related to how I handle the interceptor and promises. I came across another approach on GitHub, but it deviates from the official documentation method. While it works without the page flash, I prefer using the factory approach for cleanliness. Any insights on resolving this would be greatly appreciated!

Alternative approach found on GitHub:

var interceptor = ['$rootScope', '$q', '$log',
  function(scope, $q, $log) {

    function success(response) {
      return response;
    }

    function error(response) {
      var status = response.status;

      if (status == 401) {
        var deferred = $q.defer();
        var req = {
          config: response.config,
          deferred: deferred
        };
        scope.$broadcast('event:loginRequired');
        return deferred.promise;
      }
      // Otherwise
      return $q.reject(response);

    }

    return function(promise) {
      return promise.then(success, error);
    };

  }
];
$httpProvider.responseInterceptors.push(interceptor);

My goal isn't just to find a quick fix; I want to understand and improve my code. Thank you for your help!

Answer №1

To avoid sending a broadcast for 'event:loginRequired' from your interceptor, consider handling the location path change directly within the interceptor. Broadcasting may introduce a delay between receiving the 401 error and changing the location, potentially causing a screen 'flash' effect.

services.factory('myInterceptor', ['$q', '$rootScope', '$location',
  function($q, $rootScope, $location) {

    var myInterceptor = {

      'responseError': function(rejection) {
         if (rejection.status === 401 && $location.path() !== '/freeAccess') {
           // Redirect to login page
           $location.path('/home').replace();
         }
         // Otherwise
         return $q.reject(rejection);
      }
    };

    return myInterceptor;
  }
]);

Another approach is to make an HTTP request when your app module initializes to immediately check if the user is authorized:

myApp.config(['$httpProvider', function($httpProvider) {
  $httpProvider.interceptors.push('myInterceptor');
}])
.run(function($http) {
  // If this returns 401, your interceptor will be triggered
  $http.get('some-endpoint-to-determine-auth'); 
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve the <style> tag response and inject it into the head section of the HTML using React

Although I am aware that this may not be the best practice, it seems to be the most suitable solution for this particular case. The server response contains something like this: <style id="styles">.color_txt{color:green;}</style> I am attempt ...

is there a way to modify the background color of a div element by comparing values in javascript?

Is there a way to dynamically update the background color of a div element within a table based on values stored in a json array from a database? ...

Guide to creating varying component sizes using ReactJS and Styled Components

Is it possible to add variation to my button based on the prop 'size' being set to either 'small' or 'medium'? interface Props { size?: 'medium' | 'small'; } How can I adjust the size of the component us ...

The Homescreen.js file is not showing up as expected on the localhost/home page and is not displaying

Struggling to showcase the rooms on my hotel reservation website. Can't seem to crack it. Need some assistance, please. Spent a good 3 hours trying to figure this out. Snippet from My Homescreen.js import React ,{useState, useEffect, } from &apo ...

Troubleshooting: JQuery dropdown selection not updating image display

I am trying to create a select menu that changes the car image when a user selects a different model, but for some reason it is not working. Here is what I have tried: <h2 class="model">A6 <img src="images/3.jpg" id="image" width="544" height="2 ...

What is the best way to dynamically load content with AJAX that includes a script?

Currently, I am in the process of developing a website where I am utilizing jquery/ajax to dynamically load content onto the homepage. The test site can be found at [. While the home and about me pages load perfectly, I am encountering an issue with the pr ...

Depending on external software packages

Can you explain the potential risks associated with using third-party packages and npm in a broader sense? If I were to install a third-party package like semantic-ui-react using npm, is there a possibility that I may not be able to use it on my website i ...

Vuetify's V-alert component doesn't seem to close properly when the value property is updated, as it only displays the

I have a v-alert component that appears and disappears based on a Vuex store - it shows an error message and clears it after 4s! Below is the code for my V-alert component: The issue I am facing is that the :value attribute does not work when the value o ...

The challenge of maintaining coherence in AngularJS scopes

It's driving me crazy. Our integration with some ATSs involves sending queries and setting variables in the scope upon receiving responses. I always make sure to set the variables within a $scope.$apply() to ensure proper updating. Everything was work ...

Connect my asp.net grid view to JavaScript using AJAX

I am seeking guidance on how to bind my gridview from JavaScript in an ASP.NET web forms application. My objective is to click a linkbutton within my gridview and have it trigger a modalpopup that displays another gridview. Here are snippets of my code s ...

Angular-meteor tutorials have a method known as '/parties/insert' that is already clearly defined and explained

I am currently diving into meteor + angular and enjoying learning through ! As I was working on the 3-way data binding section, I created a folder named collections within the socially folder. In this folder, I made a file called parties.ts where I added ...

When making an Axios API request in Next.js, an error is encountered stating that the property 'map' cannot be read as it is undefined

Hey there! I've been trying to fetch data from the API endpoint in my NextJs application using axios. However, whenever I try to map over the retrieved data, NextJs keeps throwing the error message "TypeError: Cannot read property 'map' of ...

Changing the name of a specific attribute in a JSON Object for showcasing it in an HTML Table

Suppose I have fetched a list of objects from a database: [{ "id":0 ,"name":"John", "lastname":"Shell" },{ "id":1,...]; and want to display this data using the dynatable plugin: data : JSON.stringify(data) success: function(data,status){ $( ...

Looking to retrieve the dynamic "Publish Date" value from a webpage using HtmlUnit in Java?

As part of a coding exercise, I am currently working on a project that involves comparing the current system date with dates on various web pages to determine if there are any new updates. While most of the pages work as expected, there is one particular p ...

We were unable to locate the module '@reactflow/core' or its associated type declarations

After forking reactflow, I attempted to make some modifications but encountered a type error even without making any changes. My next step was to try "pnpm i @types/reactflow," but it did not resolve the issue. ...

"Upon refreshing the Angular app, the index.html file is not loaded

My angular.js application has a basic setup where the index.html file renders static content like Top/Sidebar. Inside, there is a <div ng-view></div> to display partial html files such as home.html. Upon logging into the application, both the ...

ERROR_UNSUPPORTED_ESM_URL_SCHEME - issue with next-sitemap plugin

I have a project utilizing next-sitemap with Node.js version v14.11.0. next-sitemap.config.js module.exports = { siteUrl: 'https://*****.com', generateRobotsTxt: true, robotsTxtOptions: { additionalSitemaps: [ 'htt ...

AngularJS: Utilizing UI Bootstrap Popover with the Placement Set to 'bottom-right'

I am working with UI Bootstrap and AngularJS, attempting to customize a popover to have the 'bottom-right' placement instead of the default 'bottom' or 'right'. The desired functionality is illustrated in the image below. htt ...

The initiation of jQuery animation through user interaction hinges on the completion of the preceding animation

In my project, I've designed a timeline that offers users the ability to zoom in and out by simply clicking on corresponding buttons. Since the timeline is too large to fit entirely on the screen, it is contained within a scrollable div. To ensure tha ...

What causes variables and functions to behave differently when it comes to hoisting?

I've recently been delving into some documentation and have noticed some inconsistencies in hoisting patterns within JavaScript. Consider the following examples: When it comes to functions, function abc(){ console.log("worked") } abc(); OUTPUT : ...