Serenity API Rest Assured Java Session Token Generator in Dev SSO Envs

Rahib Heydarov
6 min readNov 27, 2023

--

It took me more than ten days to figure this out and build a separate java class to be able to generate session token in each test run so I can use it as a separate util class. So the story started with the Serenity API framework that I build from scratch for our new application which has an SSO (Single Sign On) login. Before we jump into it — let me give you a little bit of brief about the Serenity API framework:

Serenity is an open-source test automation framework that facilitates the development of high-quality automated acceptance tests for web applications. It combines the capabilities of various technologies and tools to create a comprehensive testing solution. Serenity promotes Behavior-Driven Development (BDD) practices, enhancing collaboration among developers, testers, and stakeholders by using plain language specifications.

The core components of the Serenity framework include:

  1. Java: Serenity is primarily built using Java, making it compatible with Java-based projects. Java provides the backbone for writing test scripts and leveraging its robust features for automation.
  2. Maven: Maven is a powerful build automation tool used for managing dependencies and building Java-based projects. Serenity integrates seamlessly with Maven, allowing easy project setup, dependency management, and execution of automated tests.
  3. Junit: JUnit is a popular unit testing framework for Java that allows developers to write and run unit tests. Serenity utilizes JUnit for structuring and executing its test scenarios, providing detailed reports on test results and facilitating test-driven development practices.
  4. Serenity Reports: Serenity generates comprehensive and easily understandable test reports. These reports offer insights into test execution, including detailed information about test scenarios, test steps, success rates, and any failures encountered during test runs. Serenity Reports aid in understanding the test coverage and identifying issues efficiently.
  5. Serenity BDD: Serenity encourages BDD practices by offering a higher level of abstraction for writing test scenarios. It utilizes the Gherkin syntax (Given-When-Then) for writing human-readable test specifications. Serenity BDD allows collaboration among team members by providing a common language that both technical and non-technical stakeholders can understand, fostering better communication and clearer requirements.

Based on the above mentioned components Serenity framework along with the external library initiates and generates the most comprehensive, clear and coherent testing report. Here is how the Serenity Report Recipe is made:

Initiate, Observe, and Analyze the Report is the name of the game — Process encompassing test initiation, observation, and report analysis in Serenity framework.

Serenity Test Runner

Orchestrates test execution, integrating Java, JUnit, and custom utilities.

Java Test Script Initiation

Starts execution of test scripts written using Serenity’s syntax.

JUnit Libraries

Manages test script execution, following annotations and structure.

Dependency Management

Handles project dependencies specified in ‘pom.xml’, ensuring required libraries are available.

And Now about how I solved the SSO layered application login page so I can generate the session token out of Cookies:

Session tokens in API testing are unique identifiers generated by servers upon user authentication. They authenticate API requests by being included in request headers. These tokens grant access to protected API endpoints for the duration of a user’s session, ensuring secure communication between the client and server. Meanwhile, when the SSO (Single Sign On) layer is embeeded into this process then the execution of sessionToken which is created in combination with the browser user session generation and storing of the token turns into a different phase. And this is exactly guys — How I met your mother — oh ahmm no not really…

So, initial step was to be able to generate a separate class as I did in the local env whereas I do touch base with the endpoint and execute the RestAssured response and extract the Cookies out of it where I can search for the session token. Here is the sample code snippet:

import io.restassured.RestAssured;
import io.restassured.response.Response;
import io.restassured.http.Cookies;

public class TokenGenerator {

public static Cookies generateToken() {
RestAssured.baseURI = "yourLocalBaseURI";

Response response = RestAssured.given()
.redirects().follow(false)
.when()
.get("/sso/...")
.then()
.statusCode(302)
.log().all()
.extract()
.response();

return response.getDetailedCookies();
}

public static void main(String[] args) {
Cookies generatedCookies = generateToken();
System.out.println("Generated Cookies: " + generatedCookies);
}
}

Above code snippet is for the local environment and as you see it redirects from the main login page therefore there is a temporary page in between which is the SSO page and therefore we should expect 302. And if the expected temporary page is hit then we extract the response body and with the help of .getDetailedCookies() we extract the session token. In a nutshell the code snippet simulates an SSO authentication scenario where it interacts with an SSO endpoint, expects a temporary redirect, and extracts cookies (including session tokens) from the response, which are crucial for subsequent authenticated API requests. This is solely possible for an SSO simulator environment in local.

…but when it comes to the real dev environment automation of the same scenario to generate the session token things turn into a mess really quick…

With the above mentioned version to touch base with the endpoints is almost impossible. therefore need to generate a separate class with at least three to four layer automation. First two below:

public static Cookies getSessionToken() {
SessionFilter sessionFilter = new SessionFilter();

RestAssured.baseURI = "https://.dev.env.com/";

RestAssured.useRelaxedHTTPSValidation();

Response response = given()
.filter(sessionFilter)
.redirects().follow(true)
.when()
.contentType("application/json")
.get("/sso/");

htmlResponseBody = response.getBody().asString();
csrfToken += extractCSRFTokenUsingRegex(htmlResponseBody);

String jsonString = "{\"\"}";

Response response02 = given()
.filter(sessionFilter)
.redirects().follow(false)
.header("Csrftoken", csrfToken)
.contentType(ContentType.JSON)
.body(jsonString)
.when()
.post("/auth")
.then()
.statusCode(200)
.extract()
.response();

return response02.getDetailedCookies();
}

Session Initialization:

  • A SessionFilter is set up to handle session-related information.

SSO GET Request:

  • The code sends a GET request to the SSO endpoint (/sso/) to initiate the authentication process.
  • The response body (HTML) is captured.

CSRF Token Extraction:

  • Using a regular expression (extractCSRFTokenUsingRegex method not shown in the provided snippet), the code attempts to extract a CSRF token from the HTML response body. CSRF tokens are used to prevent Cross-Site Request Forgery attacks.

Authentication Request Preparation:

Authentication POST Request:

  • Another request is made to the authentication endpoint (/auth) via a POST method.
  • The extracted CSRF token is included in the header of this request (header("Csrftoken", csrfToken)).
  • The code expects a successful response with a status code of 200.
  • Cookies, likely including session tokens for further authentication, are extracted from the response.

This code snippet appears to simulate an initial SSO authentication phase by initiating a session, obtaining a CSRF token from the response, and using that token for authentication in the subsequent request. The extracted cookies may contain essential session-related information for accessing protected resources.

Final phase of the session token extractor is in the following snippet.


public static Cookies devTokenGenerator() {
String extractedCredentials = extractCredentialsFromURL(getLocationHeader());
SessionFilter sessionFilter = new SessionFilter();

RestAssured.baseURI = "https://.dev.env.com";

RestAssured.useRelaxedHTTPSValidation();

Response response = given()
.redirects().follow(false)
.when()
.contentType("application/json")
.get("/sso/...."+extractedCredentials);

return response.getDetailedCookies();
}

The method devTokenGenerator() orchestrates the retrieval of authentication tokens within a Single Sign-On (SSO) environment. Initially, it extracts specific credentials, presumably needed for authentication, from a URL using the extractCredentialsFromURL() method. Following this, the code sets up a SessionFilter and configures RestAssured to accept relaxed HTTPS validation, likely for testing purposes. Subsequently, it performs a GET request to an SSO-related endpoint by appending the extracted credentials to the endpoint URL.

This request, deliberately set to not follow redirects, aims to acquire essential tokens or session-related information. Upon receiving the response, the method extracts detailed cookies, presumably containing crucial authentication tokens or session data, and returns them for potential use in subsequent authenticated interactions within the SSO environment. Overall, devTokenGenerator() orchestrates the process of obtaining and handling essential authentication-related tokens from the SSO endpoint for further secure access to protected resources.

Sorry if it was a bit too long but I hope this will help other peers to take care of this issue easily and work on the other challenges which they will also share with world.

Good luck all automation enthusiasts out there!!!

--

--