skip to content

Negotiated Access

Credentials REST Assured must go and fetch before it can send yours: a login page it parses, a CSRF token it reads, a session id it replays. Every one of them costs an extra request per test.

part ofREST Assuredoverview, primer and where to startread it →
on this pageshow

explore

questions

8

In REST Assured, how do you carry a logged-in session id from one request to the next?

level: juniorimportance: must knowfreq 61%

answer

  1. the session is just a cookie
  2. read it off the response first
  3. sessionId() is a name lookup
  4. one filter instance, two requests
  5. JSESSIONID is only the default

basics

~20 s

Read the session id off the login response with sessionId(), then hand it to the next call as given().sessionId(value). REST Assured writes it as the JSESSIONID cookie. One SessionFilter instance added to both requests does that capture-and-replay for you automatically.

solid answer

~40 s

REST Assured offers a manual route and an automatic one. Manually, `Response.sessionId()` on the login response returns the value of the cookie named by `SessionConfig.sessionIdName()` — `JSESSIONID` unless you change it — and you replay it with `given().sessionId(id)`, which is the two-argument `sessionId(name, value)` with the name resolved from that config. Automatically, you construct one `SessionFilter`, add it to both calls with `given().filter(sessionFilter)`, and it captures the id from every response and applies it to the next request that does not already carry that cookie. So a beehive telemetry suite can `POST /apiary/session` once and then `GET /hives/h-42/telemetry` on the same session. Reach for the filter when sign-in and the read are separate steps; reach for `sessionId(...)` when you already hold the value and want it visible in the test.

code

java · 33 lines
java
import io.restassured.filter.session.SessionFilter;
import io.restassured.http.ContentType;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.Matchers.greaterThan;

class HiveTelemetrySessionTest {

    @Test
    void readsTelemetryOnTheSignInSession() {
        SessionFilter apiarySession = new SessionFilter();

        given()
            .baseUri("https://hives.example.test")
            .filter(apiarySession)
            .contentType(ContentType.JSON)
            .body("{\"keeper\":\"ada\",\"passphrase\":\"combsecret\"}")
        .when()
            .post("/apiary/session")
        .then()
            .statusCode(200);

        given()
            .baseUri("https://hives.example.test")
            .filter(apiarySession)
        .when()
            .get("/hives/h-42/telemetry")
        .then()
            .statusCode(200)
            .body("broodTemperatureC", greaterThan(30.0f));
    }
}

go deeper

for a junior

Be ready to show the two-step shape out loud: extract the id from the sign-in response, then set it on the next request. Naming JSESSIONID and given().sessionId(value) is usually enough at this level.

for a middle

Explain that sessionId() is a lookup of the cookie named by SessionConfig, not a positional read, and that a SessionFilter holds the value inside the instance you added to both calls.

for a senior

Show how you would diagnose a suite where the second call returns 401: check whether the id was captured at all, whether the cookie name matches, and whether something already set that cookie.

for a principal

Be ready to argue where the session should live for a whole suite, and what it costs when one holder is shared by more identities or more tests than it was scoped for.

## What a "session" actually is to REST Assured REST Assured has no session object of its own. What it calls a session is a single cookie travelling on the `Cookie` request header, and by default that cookie is named `JSESSIONID`. Reusing a session therefore reduces to one mechanical step: take the value the server returned on an earlier response and put it on the next request. The library gives you three holders for that value, and the only real difference between them is **who keeps it between the two calls**. - **You keep it** — `String id = response.sessionId()`, then `given().sessionId(id)` on the next call. - **A filter keeps it** — one `SessionFilter` instance added to both requests. - **The global config keeps it** — `RestAssured.sessionId`, which is folded into `SessionConfig`. None of the three performs a login. You still make the sign-in call yourself; these only decide how its result travels forward. ## Reading the id off the sign-in response `Response.sessionId()` — reachable as `post("/apiary/session").sessionId()` or as `then().extract().sessionId()` — returns the value of the response cookie whose name is `SessionConfig.sessionIdName()`. That name defaults to the constant `SessionConfig.DEFAULT_SESSION_ID_NAME`, the literal string `JSESSIONID`, and REST Assured stamps the response object with the active name at request time. It is a **name lookup**, not "whatever cookie came back first". If a beehive telemetry API sets a cookie called `APIARY_SESSION`, `sessionId()` returns `null`, and you must either configure the name or read the cookie by hand with `extract().cookie("APIARY_SESSION")`. ```java String hiveSession = given() .contentType(ContentType.JSON) .body("{\"keeper\":\"ada\",\"passphrase\":\"combsecret\"}") .when() .post("/apiary/session") .then() .statusCode(200) .extract().sessionId(); ``` ## Replaying it on the next request `given().sessionId(hiveSession)` is the one-argument form on `RequestSpecification`. It resolves the cookie name from the active `SessionConfig` and delegates to `sessionId(String name, String value)`, so it is very nearly `given().cookie("JSESSIONID", id)` — with one difference that matters later: `sessionId(...)` **replaces** any cookie already on the spec with that name, whereas `cookie(...)` appends another one. A two-argument `given().sessionId("APIARY_SESSION", hiveSession)` names the cookie for that request only and overrides the configured name. ```java given() .sessionId(hiveSession) .when() .get("/hives/h-42/telemetry") .then() .statusCode(200) .body("broodTemperatureC", greaterThan(30.0f)); ``` ## Letting a filter carry it A `SessionFilter` holds one captured id internally. Add the **same instance** to both requests: ```java SessionFilter hiveSession = new SessionFilter(); given().filter(hiveSession).contentType(ContentType.JSON).body(credentials) .when().post("/apiary/session") .then().statusCode(200); given().filter(hiveSession) .when().get("/hives/h-42/telemetry") .then().statusCode(200); ``` After the request chain it stores whatever `response.sessionId()` returned, if that is not blank, and on a later request it sets that id — but only if the request does not already carry a cookie of that name. `hiveSession.getSessionId()` hands the captured value back when you need it explicitly, and `hasSessionId()` tells you whether anything was ever captured. Constructing a fresh `new SessionFilter()` for each call is the most common way to get nothing: a filter that never saw the sign-in response has nothing to replay. ## Choosing between the three | approach | who holds the id | reach for it when | | --- | --- | --- | | `given().sessionId(id)` | a variable in your test | you already have the value and want it on the page | | `SessionFilter` | the filter instance | sign-in and the reads are separate steps | | `RestAssured.sessionId` | the global `SessionConfig` | every call in the run shares one identity | ## Things that bite in practice 1. **The cookie is not named `JSESSIONID`.** Then `sessionId()` is `null`, the filter captures nothing, and the follow-up call comes back `401`. Set `RestAssured.config = config().sessionConfig(new SessionConfig().sessionIdName("APIARY_SESSION"))`, or use a `CookieFilter`, which carries cookies by whatever name the server used. 2. **A new filter instance per request.** The capture lives in the instance; a fresh one is empty. 3. **The static outlives the test.** `RestAssured.sessionId` is process-wide until `RestAssured.reset()` runs, so a test written to assert an anonymous `401` on `/hives/h-42/telemetry` can quietly get a `200`. 4. **Nothing overwrites an id you set explicitly.** Both filters and the config value step aside when the request already names that cookie, which is convenient and occasionally invisible. ## Verifying it worked The cheapest check is to log the request cookies with `given().log().cookies()` on the second call, or to assert on the response of the sign-in with `then().cookie("JSESSIONID", notNullValue())` before you rely on the value at all. If the second request returns `401` while the first returned `200`, the question is always the same: did the id get captured, and did it get applied?

  • How would you get the captured value out of the SessionFilter to use it elsewhere?
    `SessionFilter.getSessionId()` returns the last id it caught, and `hasSessionId()` reports whether it ever caught one. Both read the same internal reference the filter applies on later requests, so a null from `getSessionId()` is proof that the capture, not the replay, is what failed.
  • What happens if you construct a new SessionFilter for every request?
    Each instance starts empty, so nothing is ever replayed and every call after the sign-in goes out anonymous. The capture lives in the instance's own field; sharing the session means sharing the object. Build one filter and add it to each request in the flow.

saying these in an interview costs you the question

  • Thinking REST Assured logs in automatically once a session exists
  • Believing sessionId() returns the first cookie in the response
  • Creating a new SessionFilter for each request in the flow
  • Assuming the session cookie is always called JSESSIONID
  • Expecting given().sessionId() to work without a prior sign-in call
open as a page

In REST Assured, how does given().csrf(path) decide to send the token as a header or a form parameter?

level: middleimportance: must knowfreq 36%

basics

~20 s

REST Assured GETs that path and scans the HTML. By default it prefers a meta tag named _csrf_header and sends the value as the X-CSRF-TOKEN header. Otherwise it uses the hidden input named _csrf as a form parameter.

open as a page

In REST Assured, auth().form(...) throws "Failed to parse login page" — how do you diagnose and fix it?

level: seniorimportance: must knowfreq 38%

basics

~20 s

REST Assured parses the login page only when FormAuthConfig is incomplete. Its auto-detection needs one form element, one text input and one password input, so a scripted or busy page fails. Pass the action and both field names explicitly.

open as a page

Your REST Assured SessionFilter returns null from getSessionId() after a successful login. Why?

level: seniorimportance: must knowfreq 47%

basics

~20 s

Almost always the session cookie is not named JSESSIONID. SessionFilter stores Response.sessionId(), which looks up the cookie named by SessionConfig.sessionIdName(), so a differently named cookie yields null, nothing is captured, and later requests go out with no session at all.

open as a page

In REST Assured, what do the three arguments of FormAuthConfig tell auth().form(...) to do?

level: juniorimportance: should knowfreq 45%

basics

~20 s

FormAuthConfig takes the form action, the username input name and the password input name. REST Assured posts your credentials to that action under those two field names. Supplying all three saves it from fetching and parsing the login page.

open as a page

In REST Assured, what does given().sessionId("abc123") actually put on the request?

level: middleimportance: should knowfreq 45%

basics

~20 s

It adds a cookie whose name comes from SessionConfig.sessionIdName(), JSESSIONID unless you changed it, carrying the value abc123. Unlike cookie(), it replaces any cookie already on the spec with that name rather than adding a second one.

open as a page

In REST Assured, when will a SessionFilter or CookieFilter refuse to apply the session it captured?

level: seniorimportance: should knowfreq 39%

basics

~20 s

Whenever the request already carries a cookie of that name. Both filters check the request specification first and stand down rather than overwrite, as does the session id value held in SessionConfig. Only given().sessionId() replaces an existing cookie outright.

open as a page

How would you choose between RestAssured.sessionId, SessionFilter and CookieFilter for a REST Assured suite?

level: principalimportance: should knowfreq 35%

basics

~20 s

Pick the narrowest holder the suite actually needs. RestAssured.sessionId is one process-wide value, a SessionFilter instance is one identity you can scope, and a CookieFilter carries every cookie the server sets when the state is more than one id.

open as a page