Search code examples
jqueryspring-securitytokencsrfcsrf-protection

How to retrive csrf token from header with jQuery when POST is only method?


I am writing Rest Api with Spring and use jQuery for the app on the front which uses this api. Session is stored in a cookie so I need CSRF protection with tokens. Token is send in a request/response header and stored in meta tags on the page. This is not full SPA, hence at the authentication and registration i send separate html pages: index.html(for authenticated users), login.html(login page), register.html(registration page). Here is the problem, i don't know how to retrieve csrf token at registration and login because first and only request is always POST but somehow i need to set csrf token on the page first. Static pages i retrieve is just by calling static resources with: window.location = '/register.html'; for example. I tried to set get request for "/token" endpoint which returns ResponseEntity with status only and fetch token from header, but then error shows in javascript that jqXHR.getResponseHeader('X-CSRF-TOKEN') is not a function.... weird. How can write method with jquery which will retrieve token from header in this case?

For example here my register.js file and how i handle fetching and attaching tokens.

In login page i have button whch redirects on registration page:

 $('#registerBtn').on('click', function () {
    window.location = '/register.html';
});
--------------------------------
And registration page is: 

    $(document).ready(function () {
    setCsrfToken();
    $('#submit').on('click', function () {
        return $.ajax({
            url: '/registration',
            type: 'POST',
            beforeSend: function (request) {
                setHeaderWithCsrfToken(request);
            },
            data: $('#registrationForm').serialize(),
            timeout: 3000
        }).then(function (data, textStatus, jqXHR) {
            console.log(jqXHR.status);
        }, function (jqXHR) {
            if (jqXHR === 422) {
                // $('#registrationForm').append(jqXHR);
                alert(jqXHR.status);
            } else {
                alert(jqXHR.status);
            }
        });
    });
});

// simple get method to retrieve response header with token
function setCsrfToken() { // TODO: how to make it reusable?
    return $.ajax({
        url: '/token',
        type: 'GET',
        timeout: 3000
    }).always(function (jqXHR) {
        getCsrfToken(jqXHR);
    });
}

function getCsrfToken(jqXHR) {
    $('meta[name="X-CSRF-TOKEN"]').attr('content', 
jqXHR.getResponseHeader('X-CSRF-TOKEN'));
    $('meta[name="X-CSRF-HEADER"]').attr('content', 
jqXHR.getResponseHeader('X-CSRF-HEADER'));
}

function setHeaderWithCsrfToken(request) {
    var token = $('meta[name="X-CSRF-TOKEN"]').attr('content');
    var header = $('meta[name="X-CSRF-HEADER"]').attr('content');
    request.setRequestHeader(header, token);
}

Solution

  • I think you use the wrong argument in the AJAX callback function. Try this:

    $.ajax({
        url: '/token',
        type: 'GET',
        timeout: 3000
    }).done(function (data, textStatus, jqXHR) {
        getCsrfToken(jqXHR);
    });