User authentication on AngularJs is only initiated on the second interaction

My personally built AngularJs user authentication system is experiencing an unusual issue. While everything seems to be working fine - I can login, check access for specific pages, etc. - there is a strange behavior with the token authentication. It seems to only run properly on the second interaction with the WebApp.

For instance, if I modify some data in localStorage (where user data is stored) and then try to access an admin page, it allows me to enter. But during the next interaction, I suddenly get redirected back to the login process.

I'm puzzled by this situation. The code I am using is as follows:

app.js

function getUser() {
    userinfo = JSON.parse(localStorageService.get("user")); //convert string to json
    $scope.userData = userinfo; //Display purpose only;
};
function checkToken() {
    var userCheckToken = JSON.parse(localStorageService.get("user"));
    $http.post('dist/php/db.php?action=token', userCheckToken)
    .success(function(res){
        userToken = res;
    }).error(function(err){
        alert(feedbackError);
    });
};

$rootScope.$on('$stateChangeStart', function(e, to) {
    if (to.data && to.data.requireLogin) {
        getUser();
        checkToken();
        if (!userinfo) {
            e.preventDefault();
            $state.go('login');
            alert("You need to be logged in");
        }
        else if (userinfo && !userToken) {
            e.preventDefault();
            userInfo = false;
            $state.go('login');
            localStorageService.clearAll();
            alert('Authentication failed');
        }
    }
});

The same issue also arises with individual functions. For example, when trying to execute an important function that requires admin privileges, the authentication process behaves similarly, failing on the first attempt.

Function:

$scope.debugTk = function() {
    checkToken();
    if (!userToken) {
        alert('Authentication failed');
    } else {
        $http.get('dist/php/db.php?action=debugTk')
        .success(function(res){
            $scope.resultDebug = res;
        }).error(function(err){
            alert(feedbackError);
        });
    }
}

Answer №1

It has been mentioned by charlieftl that your checkToken function relies on an asynchronous XHR request by default. To ensure proper execution of checkToken, you must encapsulate it within a callback function like this:

function retrieveUserData() {
    userInfo = JSON.parse(localStorageService.get("user")); //converting string to JSON
    $scope.userData = userInfo; //For display purposes only;
}
function verifyToken() {
    var userCheckToken = JSON.parse(localStorageService.get("user"));
    $http.post('dist/php/db.php?action=token', userCheckToken)
    .success(function(response){
        return response; //Making them chainable inside a promise.
    }).error(function(error){
        return errorMessage;
    });
}

$rootScope.$on('$stateChangeStart', function(event, destinationState) {
    if (destinationState.data && destinationState.data.requireLogin) {
        retrieveUserData();
        if (!userInfo) {
            event.preventDefault();
            $state.go('login');
            alert("You must be logged in");
        } else {
            verifyToken().then(function(userToken){ 

                if (!userToken) {
                    userInfo = false;
                    $state.go('login');
                    localStorageService.clearAll();
                    alert('Authentication failed');
                }
            }, function(error){
                
            });
        }
    }
});

Your debugTk function should then appear as follows: $scope.debugTk = function() { verifyToken().then(function(){ // success }, function(){ // error

        if (!userToken) {
            alert('Authentication failed');
        } else {
            $http.get('dist/php/db.php?action=debugTk')
            .success(function(response){
                $scope.resultDebug = response;
            }).error(function(error){
                alert(errorMessage);
            });
        }
    });
}

To learn more about promises, visit: https://github.com/wbinnssmith/awesome-promises

Edit: The e.preventDefault() method will not work within the promise, prompting the need to adjust your code for promises. It is advised not to include such code within a $stateChangeStart event; instead, utilize a service to manage authentication tasks.

Answer №2

As per the AngularJS documentation:

The previously used legacy promise methods success and error in $http have now been deprecated. It is recommended to utilize the standard then method instead. If $httpProvider.useLegacyPromiseExtensions is set to false, calling these methods will result in a $http/legacy error.

In relation to your query, it has been mentioned that the checkToken function operates asynchronously, hence requiring the use of promises each time this function is invoked. The $http.post result from the checkToken function should be returned:

function checkToken() {
  var userCheckToken = JSON.parse(localStorageService.get("user"));
  return $http.post('dist/php/db.php?action=token', userCheckToken).then(
    function (res) {
      userToken = res;
    },
    function (err) {
      alert(feedbackError);
    });
};

This can then be utilized as a regular promise:

$scope.debugTk = function() {
  checkToken().then(function(){
    if (!userToken) {
      alert('Authentication failed');
    } else {
      //.....
    }
  });
}

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

JSON string inside a string-type in AWS model

My goal is to create a basic model that can accept a JSON string instead of defining all variables/elements upfront. The model will have an "options" element which will hold a JSON string. Below is the structure of my model. { "$schema": "http://json-sch ...

Tips on Creating a Display None Row with a Sliding Down Animation Using Javascript

I am working with a table that has some hidden rows which only appear when the "show" button is clicked. I want to know how I can make these hidden rows slide down with an effect. Here's the snippet of my code: function toggleRow(e){ ...

The lack of a defined theme in the makeStyles for @mui/styles sets it apart from @material-ui/core

Struggling to update my material-ui from version 4.11 to version 5 and running into problems with themes. import { createTheme } from '@mui/material/styles'; import { ThemeProvider, StyledEngineProvider, } from '@mui/material/styles&apo ...

What is the most effective method for handling extremely large Long numbers in Ajax?

When it comes to Javascript, all numbers are represented as double-precision floating-point. This can result in a loss of precision when handling numbers that exceed the 64 bit Java Long datatype limit of 17 digits. For instance, a number like: 7143412520 ...

Transfer the HTML5 FullScreen (MAP) feature onto a separate display

I'm trying to integrate a leaflet map into my AngularJS Application, but I've hit a roadblock. My goal is to move the Fullscreen feature of the Map to a second screen (if one is available), allowing users to interact with the application on the ...

Is it possible to automatically refresh a webpage with the same URL when accessed on the same server?

Is there a way to trigger a URL page refresh using JS code located on the same server? For example, I have a URL page open on multiple devices connected to the same server. How can I ensure that when I click 'Update,' the page refreshes simultan ...

What is the best way to save JavaScript array information in a database?

Currently working on developing an ecommerce website. My goal is to have two select tags, where the options in the second tag are dynamically populated based on the selection made in the first tag. For example, if "Electronics" is chosen in the first tag f ...

Struggling to integrate a functional update button into my Material UI datagrid version 5.6.1

I'm facing a challenge in adding a button to the rows of my Material UI DataGrid component. Here is my DataGrid component setup: <DataGrid rows={adminStorage} columns={columns} autoPageSize getRowId={(logistics) => logistics._id ...

Restricting the number of lines within a paragraph in Angular 2

Is there a method to limit the number of lines in a <p> tag and add an ellipsis (...) at the end? Using character count for truncation doesn't work well as the width of the element varies according to device screen size. ...

Utilize Jquery to send a preset value through an ajax request

I am working on a select box functionality where the selected option is sent via ajax to a server-side script. The current setup is functioning properly. Here is the code for the select box: <select id="main_select"> <option selecte ...

After upgrading to react native version 0.73, the user interface freezes and becomes unresponsive when working with react-native-maps

After upgrading my app to the newest version of react native 0.73.x, I encountered an issue where the UI on iOS starts freezing and becoming unresponsive in production. The main screen loads react-native-maps with numerous markers, and this was not a pro ...

Enhancing Your Website with Interactive Highlighting Tags

Looking at the following html: Let's see if we can <highlight data-id="10" data-comment="1"> focus on this part only </highlight> and ignore the rest My goal is to emphasize only the highlight section. I know how to emphasize a span ...

Creating scalable React applications

Can you provide insights on the scalability of React Apps? What recommended approaches are typically employed to effectively handle and generate scalable states in web applications using Reactjs? * ...

Leveraging jQuery within a method defined in an object using MyObject.prototype.method and the 'this' keyword

Recently, I've started exploring Object-oriented programming in JavaScript and have encountered a challenge. I'm trying to define an object like the example below, where I am using jQuery inside one of the methods. I'm curious about the best ...

Is React failing to insert a span element in the client-side code?

First off, I'm a beginner in the world of react and recently tackled this guide on creating a real-time Twitter stream To cut to the chase, I wanted to include a link next to each tweet that followed its text. To achieve this, I simply added props to ...

Utilizing Various Perspectives in Angular with the ng-view Directive

Can anyone assist me with a challenge I'm facing regarding views? While this is a hypothetical scenario and there's no code available to review... Let's imagine a simple site with three routes: /signin /users /users/:id Upon accessing t ...

Tips for implementing a delay in jQuery after an event occurs

I am working with text boxes that utilize AJAX to process user input as they type. The issue I'm facing is that the processing event is quite heavy. Is there a way to make the event wait for around 500ms before triggering again? For example, if I type ...

Don't delay in fulfilling and resolving a promise as soon as possible

Currently, I am facing an issue with a downstream API call that is returning a Promise object instead of resolving it immediately. This is how I am making the downstream call: const response = testClient.getSession(sessionId); When I console.log(response ...

Java script pop-up notifications always show up

This Text Goes Above the Form <?php require_once "core-admin/init-admin.php"; if( !isset($_SESSION['admin_username']) ){ $_SESSION['msg'] = 'page cannot be opened'; header('Location:admin_login.php&ap ...

Guide on adding markers using Google Maps API based on specific criteria

Hi there, I need assistance in developing the following requirements. In my database, there is parking lot information. Let's say each parking lot has two attributes: id and address. Via a spring controller, I am passing a list of parking lots t ...