ThatAwesomeCoder ThatAwesomeCoder - 2 months ago 27
HTTP Question

Angular, Response to Preflight Request

I have an interceptor that handles all my requests on my controllers. I have a back-end web API that implements a refresh token but when I try to refresh my token and continue with the request being made I get "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." And the token request gives me {"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
$rootScope.globals.showLoading = true;

$rootScope.globals.showLoading = false;
var data = {
access_token: accessToken,
expires_in: time_expires,
refresh_token: refreshToken
};
authenticationService.DoLogin(data, true);

//authenticationService.GetAuthenticationProperties().success(function (data) {
// $rootScope.globals.showLoading = false;
// data.access_token = accessToken;
// authenticationService.DoLogin(data, true);
//}).error(function () {
// $rootScope.globals.showLoading = false;
//});
} 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
$rootScope.globals.showLoading = true;

$rootScope.globals.showLoading = false;
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());
//Set the access token
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 help would much be appreciated.

Answer

I have managed to fix my own problem, on the interceptor I injected the authService functions and reset the localstorage access, I then added and "ALLOW OPTION" for option requests to be resolved on 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);
        }