Angular Reacts to Pre-Flight Inquiry

I'm facing an issue with my interceptor that manages requests on my controllers. The back-end web API has a refresh token implementation, but when I try to refresh my token and proceed with the request, I encounter the error message "Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 400." Additionally, the token request returns {"error":"invalid_clientId","error_description":"ClientId should be sent."}

Interceptor:

var appInterceptors = angular.module("auth-Interceptor", ["ngRoute", "angular-loading-bar"]);

/* ==== Bearer token headers configuration ==== */
appInterceptors.factory("authentication-interceptor", ["$q", "$injector", "$rootScope", "$location", "cfpLoadingBar", function ($q, $injector, $rootScope, $location, cfpLoadingBar) {
    var inFlightAuthRequest = null;
    var deferred = $q.defer();
    return {
        // On request success
        request: function (config) {
            config.headers = config.headers || {};
            if ($rootScope.globals.accessToken != null) {
                config.headers.Authorization = 'Bearer ' + $rootScope.globals.accessToken;
            }
            // Return the config or wrap it in a promise if blank.
            return config || $q.when(config);
        },
        requestError: function (rejection) {
            debugger; //return debugger for more info

            return rejection;
        },
        responseError: function (rejection) {
            debugger;
            if (rejection.status === 401) {
                var refreshToken = window.localStorage.getItem("refreshToken");    //log the user in if there is an refresh token

                $injector.get("$http").post(
                        $rootScope.globals.apiPath + "/accessControl/token",
                        { client_id: "id", grant_type: "refresh_token", refresh_token: refreshToken },
                        { 'Content-Type': 'application/x-www-form-urlencoded' }
                    )
                    .then(function (data) {
                        inflightAuthRequest = null;
                        if (data.access_token != undefined && data.refresh_token != undefined) {
                            window.localStorage.setItem("refreshToken", data.refresh_token);
                            window.localStorage.setItem("accessToken", data.access_token);
                            window.localStorage.setItem("rememberMe", true);
                            window.localStorage.setItem("time_expires_in", data.expires_in);
                            $injector.get("$http")(rejection.config).then(function (resp) {
                                deferred.resolve(resp);
                            }, function (resp) {
                                deferred.reject();
                            });
                        } else {
                            deferred.reject();
                        }
                        return $q.reject(rejection);
                    }, function (response) {
                        deferred.reject();
                        authService.clear();
                        $injector.get("$state").go('/login');
                        return;
                    });
                return deferred.promise;
            }
        }
    };
}]);

appInterceptors.config(["$httpProvider", function ($httpProvider) {
    $httpProvider.interceptors.push("authentication-interceptor");
}]);

Service:

jobManagerApp.factory("authenticationService", ["$rootScope", "$http", "$location", function ($rootScope, $http, $location) {
    return {
        Login: function (username, password) {
            return $http({
                url: $rootScope.globals.apiPath + "/accessControl/token",
                method: 'POST',
                data: "userName=" + encodeURIComponent(username) +
                      "&password=" + encodeURIComponent(password) +
                      "&Scope=" + "website" +
                     "&grant_type=password" +
                     "&client_id=id",
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
            });
        },
        RefreshToken: function (refreshtoken) {
            return $http({
                url: $rootScope.globals.apiPath + "/accessControl/token",
                method: 'POST',
                data: "client_id=" +
                      "&grant_type=refresh_token" +
                      "&refresh_token=" + refreshtoken,
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
            });
        },
        LogOut: function () {
            return $http({
                url: $rootScope.globals.apiPath + "/accessControl/logout",
                method: "POST"
            });

        },
        DoLogin: function (data, rememberMe) {

            //save the tokens
            if (rememberMe == true) {
                window.localStorage.setItem("refreshToken", data.refresh_token);
                window.localStorage.setItem("accessToken", data.access_token);
                window.localStorage.setItem("rememberMe", true);

            } else {
                window.localStorage.removeItem("refreshToken");
                window.localStorage.removeItem("accessToken");
            }

            //set the global values for user
            $rootScope.globals.accessToken = data.access_token;
            $rootScope.globals.refreshToken = data.refresh_token;

            //hide the menu items for which the users does not have access rights
            $rootScope.HideMenuItems();

            //navigate to the page where the user originally wanted to go (returnLocation) or to the default page
            var gotoLocation = $rootScope.globals.returnToLocation;
            if (gotoLocation != "") {
                $location.path(gotoLocation);
                $rootScope.globals.returnToLocation = "";
            } else {
                //go to default page
                $location.path("/home");
            }

            //set the logged in value only after the navigation has taken place as it is linked to the ng-show/hide of the toolbar and menu
            $rootScope.globals.isLoggedIn = true;
        }
    };
}]);

Login:

jobManagerApp.controller("loginController", ["$scope", "$rootScope", "$location", "authenticationService", function ($scope, $rootScope, $location, authenticationService) {

    $scope.LoginButtonDisabled = false;
    $scope.LogonName = null;
    $scope.Password = null;
    $scope.Error = "";
    $scope.Version = ($rootScope.globals.version.indexOf("-") == -1) ? $rootScope.globals.version : $rootScope.globals.version.substring(0, $rootScope.globals.version.indexOf("-"));

    $scope.Login = function () {
        $scope.LoginButtonDisabled = true;
        if ($scope.LogonName !== null && $scope.Password !== null) {
            //attempt login
            authenticationService.Login($scope.LogonName, $scope.Password)
                .success(function (data) {
                    $scope.LoginButtonDisabled = false;
                    if (data.access_token != undefined) {
                        //Time Expires
                        window.localStorage.setItem("time_expires_in", data.expires_in);
                        //Time user logged in
                        window.localStorage.setItem("time_logged_in", new Date().getTime());
                        //do the actual login
                        authenticationService.DoLogin(data, $scope.RememberMe);
                    }
                    else if (data.error_description != undefined) {
                        $scope.Error = data.error_description;
                    }
                    else {
                        $scope.Error = "Unexpected error occurred!";
                    }
                })
                .error(function (data, status, headers, config) {
                    $rootScope.globals.accessToken = null;
                    window.localStorage.removeItem("accessToken");
                    window.localStorage.removeItem("refreshToken");
                    $scope.LoginButtonDisabled = false;
                });
        } else {
            $scope.Error = "Enter a username and password!";
            $scope.LoginButtonDisabled = false;
        }        
    };

    var accessToken = window.localStorage.getItem("accessToken");     //log the user in if there is an access token
    var refreshToken = window.localStorage.getItem("refreshToken");    //log the user in if there is an refresh token
    var time_expires = window.localStorage.getItem("time_expires_in");     //Time token expires
    var time_logged_in = window.localStorage.getItem("time_logged_in");     //Time user logged in

    var time = new Date().getTime();     //CurrentTime
    var tokenExpired;    //variable to be used to setExpired 

    if (((time / 1000) - (time_logged_in / 1000)) >= time_expires) {
        tokenExpired = true;
    } else {
        tokenExpired = false;
    }

    //Log test
    console.log("Time left: " + (time_expires - ((time / 1000) - (time_logged_in / 1000))));
    console.log(refreshToken);
    //login
    if (accessToken != null && tokenExpired == false && refreshToken != null) {
        $rootScope.globals.accessToken = accessToken; //set this for the auth-interceptor to do its work
        
        var data = {
            access_token: accessToken,
            expires_in: time_expires,
            refresh_token: refreshToken
        };
        authenticationService.DoLogin(data, true);
    } else if (refreshToken != null) {
        //request a new access token
        authenticationService.RefreshToken(refreshToken)
            .success(function (data) {
                if (data.access_token != undefined && data.refresh_token != undefined) {
                    $rootScope.globals.accessToken = data.access_token; //set this for the auth-interceptor to do its work
                    $rootScope.globals.refreshToken = data.refresh_token //Set the new refresh token 
                    
                    var data = {
                        access_token: data.access_token,
                        refresh_token: data.refresh_token,
                        expires_in: data.expires_in
                    };

                    //Renew the time logged in and the time time_expires
                    //Time Expires
                    window.localStorage.setItem("time_expires_in", data.expires_in);
                    //Time user logged in
                    window.localStorage.setItem("time_logged_in", new Date().getTime());
                    tokenExpired = false //renew to false;

                    authenticationService.DoLogin(data, true);
                }
            })
            .error(function (data, status, headers, config) {
                $rootScope.globals.accessToken = null;
                window.localStorage.removeItem("accessToken");
                window.localStorage.removeItem("refreshToken");
                $scope.LoginButtonDisabled = false;
            });
    }
}]);

Any assistance would be greatly appreciated.

Answer №1

I successfully resolved my issue by updating the interceptor to include authService functions and resetting localstorage access. Additionally, I implemented an "ALLOW OPTION" for option requests to be handled in my web API:

Interceptor

appInterceptors.factory("authentication-interceptor", ["$q", "$injector", "$rootScope", "$location", "cfpLoadingBar", function ($q, $injector, $rootScope, $location, cfpLoadingBar) {
    return {
        // On request success
        request: function (config) {
            config.headers = config.headers || {};
            if ($rootScope.globals.accessToken != null) {
                config.headers.Authorization = 'Bearer ' + $rootScope.globals.accessToken;
            }
            // Return the config or wrap it in a promise if blank.
            return config || $q.when(config);
        },
        requestError: function (rejection) {
            debugger;

            return rejection;
        },
        responseError: function (response) {
            // error - was it 401 or something else?
            if (response.status === 401) {
                var deferred = $q.defer(); // defer until we can re-request a new token

                var accessToken = window.localStorage.getItem("accessToken");
                var refreshtoken = window.localStorage.getItem("refreshToken");

                // Get a new token... (cannot inject $http directly as will cause a circular ref)
                $injector.get("authenticationService").RefreshToken(refreshtoken).then(function (loginResponse) {
                        if (loginResponse) {    
                        console.log(loginResponse);
                        $rootScope.globals.accessToken = loginResponse.data.access_token; // we have a new acces token - set at $rootScope
                        $rootScope.globals.refreshToken = loginResponse.data.refresh_token; // we have a new refresh token - set at $rootScope
                        //Update the headers
                        window.localStorage.setItem("accessToken", loginResponse.data.access_token);
                        window.localStorage.setItem("refreshToken", loginResponse.data.refresh_token);
                        window.localStorage.setItem("rememberMe", true);
                        //Time Expires
                        window.localStorage.setItem("time_expires_in", loginResponse.data.expires_in);
                        //Time user logged in
                        window.localStorage.setItem("time_logged_in", new Date().getTime());

                        // now let's retry the original request - transformRequest in .run() below will add the new OAuth token
                        $injector.get("authenticationService").ResolveDeferred(response.config).then(function (defResp) {
                            // we have a successful response - resolve it using deferred
                            deferred.resolve(defResp);
                        }, function (defResp) {
                            deferred.reject(); // something went wrong
                        });
                    } else {
                        deferred.reject(); // login.json didn't give us data
                    }
                }, function (response) {
                    deferred.reject(); // token retry failed, redirect so user can login again
                    $location.path('/login');
                    return;
                });
                return deferred.promise; // return the deferred promise
            }
            return $q.reject(response); // not a recoverable error
        }
    };
}]);

AuthenticationService

 RefreshToken: function (refreshtoken) {
            return $http({
                url: $rootScope.globals.apiPath + "/accessControl/token",
                method: 'POST',
                datatype: 'jsonp',
                data: "client_id=id" +
                      "&grant_type=refresh_token" +
                      "&refresh_token=" + refreshtoken,
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
            });
        },
        LogOut: function () {
            return $http({
                url: $rootScope.apiPath + "/acess/logout",
                method: "POST"
            });

        },
        ResolveDeferred: function (config) {
            return $http(config);
        },

API

 public override Task MatchEndpoint(OAuthMatchEndpointContext context)
        {
            if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
            {
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
                context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization" });
                context.RequestCompleted();
                return Task.FromResult(0);
            }

            return base.MatchEndpoint(context);
        }

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

Utilizing repl.it for a database in Discord.js

I've created a database script on repl.it and it seems to be functioning properly. However, I keep encountering null values in the users' database. Here's my code snippet: client.on("message", async (message) => { if (messag ...

The AJAX call is not being identified correctly

I am facing an issue with my search result pagination wherein the page reloads instead of loading via AJAX when using the pagination. The search results are correctly loaded through partial view with AJAX, but the pagination triggers a non-ajax request cau ...

Unable to install react-dom/test-utils using npm

I recently included two libraries in my package.json "devDependencies": { ... "react-dom/test-utils": "*", "react-test-renderer/shallow": "*" }, These were recommended by the React documentation to align with version 16 of the React ecosy ...

Leveraging JSON for fetching distinct identifiers from a database

When a user hovers over a parent span containing an empty img tag, I use JSON and jQuery to retrieve the src of the image from the database. The issue arises when the retrieved src is applied to all looped span images on the same page, instead of each imag ...

When using VueJS to load an SVG file based on a variable within a for loop and utilizing v-html, the result returned is "[

I'm faced with a challenge of loading SVGs saved in separate files based on the content within a loop. Upon page load, what I see is: Hey there, check out my code snippet below: <div v-for="(rec, index) in stats" :key="index" > <div ...

CakePHP and Legacy Ajax Script Integration

I have an old script that I want to integrate into a cakephp application. The script uses $_POST and I'm not very experienced with this, so I need some assistance with the integration. This is what the script consists of: THE JAVASCRIPT: prototype ...

Having trouble uploading a file to Box via Node.js

Struggling to figure out why my files won't upload to box using Node.js. Every time I try, this error message pops up: Error: cannot POST /api/2.0/files/content (400) Here's the code snippet in question. I've confirmed that all necessary ...

AngularJS Class Confirmation Button

I'm currently working on implementing a "confirm" button for users of my website to see after clicking a specific button, using an angularJS class. Below is the code snippet I have written: class TodosListCtrl { constructor($scope, $window){ $s ...

What is the best location for the frontend server code within an application built using create-react-app?

After using create-react-app to create my app, I am looking to log messages in the server console. Any advice on how to achieve this? I attempted adding index.js to the root folder and creating a server folder, but so far it hasn't been successful. ...

Discovering indistinguishable "Feeling Fortunate" inquiries

My goal is to create a program on my website that can compare the top search results for different queries. For instance, I want it to recognize that the top search result for "twelve" and "12" is the same, leading to https://en.wikipedia.org/wiki/12_(numb ...

Troubleshooting an AngularJS basic application that is not functioning properly and exploring techniques for debugging Angular bugs

After successfully implementing a code example, I tried to make a minor modification by adding an application and encountered issues. It's a very basic example, but I can't seem to figure out what went wrong. Could you please help me understand: ...

Displaying the currently logged in user's name with NodeJS/ExpressJS/Passport

In my quest to showcase the username for logged-in users (function 3), I encountered a dilemma. Initially, only function 1 existed in my codebase. To address this issue, I made modifications and introduced function 2 to facilitate displaying usernames of a ...

The action is undefined and cannot be read for the property type

Using the React+Redux framework, I encountered an error: https://i.sstatic.net/0yqjl.png During debugging, the server data successfully reached the state, but the action was empty: https://i.sstatic.net/41VgJ.png Highlighted below is a snippet of my co ...

Passing large arrays of data between pages in PHP

I'm facing a challenge where I need to pass large arrays of data between pages. Here's the situation: Users input their Gmail login details in a form, which is then sent to an AJAX page for authentication and contact retrieval. If the login fail ...

What is the process for adding JSON data to a dropdown menu using PHP AJAX?

I am trying to populate a select html element with data from a list of JSON results. Here is the code I have attempted: JSON output: jquery loop on Json data using $.each {"Eua":"Eua","Ha'apai":"Ha'apai",& ...

Is jQuery capable of appropriately escaping my quotes?

Currently, I am utilizing $.cookie() to retrieve all the values from a cookie which are stored in JSON format: var properties = $.cookie('params'); The output of properties is: {"distinct_id": "13f97d6600b42e-000e6293c-6b1b2e75-232800-13f97d66 ...

open a new window with a reference based on the name

In order to obtain a reference to the currently open window, I utilize the following code: var refWindow = window.open("mypage1", "name_mypage"); If I wish to close the window, I simply use this command: refWindow.close(); When I refresh the screen (by ...

Unable to locate a contact within the state

Currently, I am diving into the world of React by exploring its documentation and challenging myself to build a small chat application. While I have a good grasp on the basics, I am encountering some issues with states. Within my code, there is an object ...

Is ng-repeat failing to bind values in the Dom?

When loading data dynamically to ng-repeat, I am encountering an issue where the data is not binding to ul. Typically, this can happen with duplicate indexes, but in my case I'm unsure of what's causing it. Any suggestions? main.html <div> ...

What is the significance of the any type in Typescript?

As I delve into learning Typescript, a question arises in my mind. I find myself pondering the purpose of the any type. It seems redundant to specify it when every variable essentially acts as an "any" type by default. Consider this scenario where the out ...