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

Is it necessary for the error event of xmlhttprequest to include an error message?

Currently, I am in the process of developing an AJAX request within a Firefox extension. The following code snippet illustrates my approach: function GetMenu(){ var oReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(); ...

How to nullify the valueChanges pipe in Angular RxJS until the observable is resolved

A challenge I am facing is piping the valueChanges from a select element to trigger the appropriate API request and displaying a spinner until the response is received. Additionally, I am trying to utilize publish() and refCount() methods so that I can use ...

In search of a screenplay that mirrors a specific scenario

I recently came across this website: One interesting feature is that all the content loads dynamically on the same page. Instead of loading separate pages, it pauses to fetch the content before smoothly scrolling to the correct section. ...

JavaScript is sending a variable to a .php file, which then returns data based on the variable. The information is displayed using JavaScript and AJAX, along with triggers set using the bind('input', function

Currently, I am attempting to create a form that automatically populates some input fields when there is a change. However, I am struggling to understand how to send the #url field to my PHP script. Any insights or suggestions would be greatly appreciated. ...

Navigate to the grandchild state with a specific parameter using ui-router's sref in the

Is there a way to create a sref that will navigate me to a grandchild state while also having a URL parameter for the child state? Check out this pseudo code example: .state( 'foo', {url:'/', template:'<div ui-view></div ...

Is there a way to stop TD from going to the next line?

I am using Javascript to dynamically generate a table and I want it to extend beyond the boundaries of the page. When I manually create the table, it works fine and extends off the page, but once I put it into a loop, the <td> elements wrap onto a se ...

Building an anchor tag that employs the HTTP DELETE method within an Express.js app

Recently, I delved into using express.js with handlebars.js as my template engine. One task I wanted to tackle was creating a delete link that followed RESTful principles and used the HTTP DELETE verb instead of GET. After some trial and error, I discover ...

Creating a new list by grouping elements from an existing list

I have successfully received data from my API in the following format: [ {grade: "Grade A", id: 1, ifsGrade: "A1XX", ifsType: "01XX", points: 22, type: "Type_1"}, {grade: "Grade B", id: 2, ifsGrade: &quo ...

WebRTC error encountered: Unable to add ICE candidate to 'RTCPeerConnection'

Encountering a specific error in the browser console while working on a project involving p2p video chat. The error message is Error: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': The ICE candidate could not be added.. Int ...

Exploring the differences between two CSS style attributes using JQuery

Can someone help me with a quick question about CSS? I need to compare two style attributes, specifically margin-left. If the margin-left is less than 500px, I don't want to make any changes. However, if it's greater than 500px, I want to add ano ...

Interacting with API through AngularJS $http.get

I am a beginner in AngularJS and I am trying to grasp its concepts by studying example codes. Currently, I have found an interesting code snippet that involves the $http.get function. You can find it here: I attempted to replace the URL with my own, but ...

D3 Chart: What is the best way to insert images into a node?

Within the jsFiddle demo provided in this query, there is code displayed. I am curious about how I can assign images to each node. var graph = { "nodes":[ {"name":"1","rating":90,"id":2951}, ] } You can access my jsFiddle Demo through this ...

Determine the Nautical Miles Between Various Coordinate Points using React

Does anyone have a solution for calculating nautical miles using react? Let's say I have this object: const calculateDistance = [ {point_id: 1, long: longitude , lat: latitude}, {point_id: 2, long: longitude , lat: latitude}, {point_id: 3, ...

Using the Gmail API to retrieve the access token details by extracting the "code" parameter from the URL of a pop-up window

I am currently in the process of authenticating Gmail using OAuth2 for my web application. Upon receiving a URL from the server, the client opens a pop-up window with the following code: var win = window.open(data.google_oauth_url, `<h1>Gmail ...

Identifying and recording duplicate values within an array using object transformation in JavaScript

I currently have an array structured like this: uniqueCount = [a,a,b,c,d,a,a]; I'm trying to figure out how many occurrences of each element (a, b, c) are in the array. I'd like to display the results in the form of an array of objects: [{key ...

I am attempting to implement an Express static middleware as demonstrated in this book, but I am having trouble understanding the intended purpose of the example

I'm currently studying a chapter in this book that talks about Express, specifically concerning the use of express.static to serve files. However, I'm encountering an issue where the code catches an error when no file is found. I've created ...

Using Javascript or Jquery, you can submit a form without the need for a button

I'm attempting to submit a form without using a button by invoking a JavaScript function and processing the form with JQUERY/PHP. My goal is for the form to be submitted silently on the backend without causing the page to reload. However, I keep encou ...

Reactjs Invariant Violation caused by the npm package (react-loader)

I'm currently attempting to integrate react-loader into my react component. This is the code snippet I'm using: /** @jsx React.DOM */ var Loader = require('react-loader'); var DisplayController = React.createClass({ // etc ...

Unable to activate Knockout data-bind using jQuery

I'm developing a plugin using jQuery and knockout.js. Currently, I have a scenario where I need to manipulate radio buttons with knockout data-bindings. However, I've encountered an issue when trying to uncheck a radio button by clicking another ...

Updating the row by substituting the content of two columns with the data retrieved from the action class

In my JSP page, there is a table with a refresh button on each row. Upon clicking the refresh button, a JavaScript/AJAX call is made to an action class to retrieve the values of status and number of records for that row, which are then displayed in the cor ...