1

Currently by using the setTimeout method I am able to successfully do what i want to. But this is not the ideal approach.

When we send a login request, we want to get the CSRF token from the response header and use it insubsequent http calls. The http.get() request does that for us(it sets the login's response header into request header). After that we want to do a post request again based on the response we get from the http.get() request inside the login callback.

The problem here is that the code gets executed before the browser finishes, setting the CSRF token received from response into the request header. In order to overcome this problem, I added a setTimeout function. But I don't really like the approach of adding a hardcoded delay.

Is there any efficient way of doing this?

        app.controller('loginCtrl', function ($scope, $http, $location, $cookies, $q, Cart,$rootScope)
{
        var defer = $q.defer();

        $scope.submit = function(){

                $http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded;application/JSON";
                //login
                $http.post('http://127.0.0.1:8000/api/user/login/', JSON.stringify({'email': $scope.email, 'password': $scope.password})
                ).success(function(data){

                defer.promise.then(function(){
                //need to get a cart to check if a cart is created
                    Cart.getCart(function(data){


                    defer.promise.then(function(){
               //if cart not created create one
                        if(data.length == 0){
                            setTimeout(function(){
                                $http.defaults.headers.common["X-CSRFToken"] = $cookies.csrftoken;
                                Cart.addCart(function(data){
                                    alert('cart successfully created \n ' + JSON.stringify(data));
                                    $rootScope.cartId = data[0].pk
                                    $rootScope.cart = data[0].fields;
                                    $location.path('/products');
                                }, function(error){
                                    alert('addcart failed');
                            });
                            },300);

                        } else {
                            $rootScope.cartId = data[0].pk;
                            $rootScope.cart = data[0].fields;
                            $location.path('/products');
                        }

             })

                }, function(error){
                    alert('getcart failed');
                });
            })
                //need code to get the cookies, still dont know how

                }).error(function(data){
                alert('failure');
                });


        defer.resolve();        
        };
});
3
  • I can't make the code tie up with what is described in words. Commented Dec 26, 2013 at 5:51
  • @Beetroot-Beetroot I can try to explain the problem i work on this project. We send a POST request to our Django application to login. After the login we send a GET the user's shopping cart and if there is no shopping cart for the user we create a new one(you can see that in the above code we are checking data.length == 0). If there is no cart we POST a request to create cart. In Django we have to set X-CSRFToken on POST request. If we do not call addCart fuction call inside the setTimeout function,the X-CSRF on the POST request is not the same as the one set on login response and gives 401err Commented Dec 26, 2013 at 6:51
  • Ah right, the code is a bit confusing. Presumably defer was introduced in an attempt to make the thing work. As far as I can tell, it's not necessary and can be safely removed. What's left looks like it should work but it would appear that there's some sort of race effect in setting $http.defaults.headers.common["X-CSRFToken"]. Very odd because setting a default header should be reliably synchronous. Commented Dec 26, 2013 at 19:10

2 Answers 2

1

Maybe you could intercept the response, parse the headers and set the token manually.

Something like this:

module.factory('xsrfTokenInterceptor', function ($q, $http) {
    return {
        'response': function (response) {
            var cookies = response.headers("Set-Cookie");
            var token = someCrazyParsing(cookies);
            $http.defaults.headers.common["X-CSRFToken"]=token;
            return response || $q.when(response);
        }  
    };
});
module.config(function($httpProvider){
    $httpProvider.interceptors.push('xsrfTokenInterceptor')
})

EDIT NEW APPROACH

Maybe something like this?

module.factory('LoginService', function ($q, $http) {
    var login = function (email, password) {
        var defered = $q.defer();
        $http.post('http://127.0.0.1:8000/api/user/login/', {
                'email': email,
                'password': password
        }).success(function (data, status, headers, config) {
            var cookies = headers("Set-Cookie");
            var token = someCrazyParsing(cookies); //<-- Your magic here
            $http.defaults.headers.common["X-CSRFToken"] = token;
            defered.resolve(data);
        }).error(function (data, status, headers, config) {
            defered.reject(data);
        });
        return defered.promise;
    };
    return {
        login: login
    };
});

module.controller("LoginCtrl", function ($scope, LoginService, Cart) {
    $scope.submit = function () {
        LoginService.login($scope.email, $scope.password).then(function (data) {
            Cart.getCart(function (data) {});
        });
    };
});
Sign up to request clarification or add additional context in comments.

5 Comments

get the following error: "Uncaught Error: [$injector:cdep] errors.angularjs.org/1.2.5/$injector/…" This error pops up on the console when we load up the login page and the whole partial stops showing.
Hmm, interesting, but makes sense, because the interceptor depends on http but http is not created yet at the time of the interceper creation.
based on the new solution, i cannot invoke the Cart.getCart() api once the user has finished logging in. I can see in the network that login succeeded with status 200.
Fairly old question, but it seems this solutions does not work, i.e. even though spying in the headers I can see "Set-Cookie", headers("Set-Cookie"); is null.
You can achieve the crazy cookie parsing functionality by calling the $cookiesProvider.$get() function again to regenerate a new local $cookies. I did this by shamefully creating a local reference to $cookiesProvider: var cookiesProvider_ref = null; app.config( function($cookiesProvider) {cookiesProvider_ref = $cookiesProvider });. I then use cookiesProvider_ref in my login success() callback to set the $http csrf token header field again.
0

Does it work if you configure it globally?

module.config(function ($httpProvider) {
    $httpProvider.defaults.xsrfHeaderName = "X-CSRFToken";
    $httpProvider.defaults.xsrfCookieName = "csrftoken";
});

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.