GrzegorzD
GrzegorzD

Reputation: 73

Restassured CSRF protection with X-XSRF-TOKEN (like angularjs) header

I'm trying to write a web integration test with restassured spring security authentication. The application uses AngularJS and Spring Boot. Since I use AngularJS the CSRF protection is done with X-XSRF-TOKEN header and XSRF-TOKEN cookie (as I understand its default for angular).

How can I configure restassured to generated and send this token with form authentication ? Right now I have something like this:

given().auth().form("user", "password", new FormAuthConfig("login", "username", "password").sendCsrfTokenAsHeader()).when().get("/index.html").then().log().all().statusCode(200);

But in the logs I see that CSRF token is invalid when posting credentials to /login .

Upvotes: 4

Views: 6242

Answers (3)

devopsix
devopsix

Reputation: 101

Use a RestAssured filter to retrieve and inject the token when needed.

Given the server's response to GET /login always includes the token cookie. Then a solution which keeps the actual test code clean could look like this:

package com.example;

import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import io.restassured.RestAssured;
import io.restassured.authentication.FormAuthConfig;
import io.restassured.filter.*;
import io.restassured.response.Response;
import io.restassured.specification.*;

@ExtendWith(SpringExtension.class)
@TestInstance(PER_CLASS)
@SpringBootTest(webEnvironment = DEFINED_PORT)
public class CsrfSampleTest {

    @BeforeAll
    public void addCsrfCookieFilter() {
        RestAssured.filters(new Filter() {
            @Override
            public Response filter(FilterableRequestSpecification requestSpec,
                    FilterableResponseSpecification responseSpec, FilterContext ctx) {
                String csrfToken = requestSpec.getCookies().getValue("XSRF-TOKEN");
                if (csrfToken == null) {
                    csrfToken = RestAssured.given().noFilters().get("/login").cookie("XSRF-TOKEN");
                }
                requestSpec.replaceHeader("X-XSRF-TOKEN", csrfToken);
                return ctx.next(requestSpec, responseSpec);
            }
        });
    }

    @Test
    public void test() {
        RestAssured.given()
            .auth().form("user", "password", new FormAuthConfig("login", "username", "password"))
        .when()
            .get("/index.html")
        .then()
            .statusCode(200);
    }
}

Upvotes: 0

Slava Babin
Slava Babin

Reputation: 728

You need to do 2 GET before post to use spring security CSRF protection in your rest client and test class.

  1. Make a GET request to login. This will return the JSESSIONID token and XSRF-TOKEN tokens. If you use the returned XSRF-TOKEN to POST it will fail, because we got it using empty/false JSESSIONID.
  2. Get a useful XSRF-TOKEN from the second GET, using JSESSIONID from previous request.
  3. Now you can use XSRF-TOKEN for your POST.

Example how to use CSRF protection with X-XSRF-TOKEN in rest assured:

//1) get sessionId
Response response =
        given().auth().preemptive().basic(userName, userPassword).contentType(JSON).
        when().get(PREFIX_URL + "/users/user").
        then().log().all().extract().response();
String jsessionidId =  response.getSessionId();//or response.cookie("JSESSIONID");

//2) get XSRF-TOKEN using new/real sessionId
response =
        given().
        sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
        contentType(JSON).
        when().get(PREFIX_URL + "/users/user").
        then().log().all().extract().response();

//3) post data using XSRF-TOKEN
given().log().all().
        sessionId(jsessionidId).//or cookie("JSESSIONID", jsessionidId).
        header("X-XSRF-TOKEN", response.cookie("XSRF-TOKEN")).
        queryParam("pos",pos.getId()).
        queryParam("date",date).
        queryParam("group_id",itemGroup.getId()).
        body(orderItems).
        contentType(JSON).
when().
        post(PREFIX_URL + "/orders/orderitems").
then().
    log().all().assertThat().statusCode(200);

Upvotes: 3

andolsi zied
andolsi zied

Reputation: 3791

Some delay in response but I hope it helps someone

Response loginResponse = given().contentType(APPLICATION_JSON).
    param(USERNAME, "").
    param(PASSWORD, "").
    when().post(LOGIN_PROCESSING_URL).then().log().all().
    extract().response();

given().contentType(APPLICATION_JSON).
  cookie("XSRF-TOKEN", loginResponse.cookie("XSRF-TOKEN")).
  header("X-XSRF-TOKEN", loginResponse.cookie("XSRF-TOKEN")).
  sessionId(loginResponse.getSessionId()).
  when().post(USER_PATH).
  then().log().all().statusCode(CREATED.value());

Upvotes: 1

Related Questions