Introduction

In our REST Assured series, we’ve covered authentication methods like Digest and Form Authentication, Basic Authentication, and GPath Expressions, equipping you with tools to test secured APIs. Now, we’ll explore three advanced authentication mechanisms: OAuth 2.0 Authentication, API Key Authentication, and Bearer Token Authentication. This guide demonstrates how to implement these in REST Assured, with practical examples and best practices. It’s designed for beginners and experienced developers, ensuring you can confidently test modern, secured APIs.

Key Point: OAuth 2.0, API Key, and Bearer Token Authentication are widely used in modern APIs, enabling secure access to protected resources. REST Assured simplifies their implementation for robust API testing.

Understanding the Authentication Mechanisms

Let’s define each authentication type:

  • OAuth 2.0 Authentication: A token-based authorization framework where clients obtain an access token via a grant type (e.g., Client Credentials, Authorization Code) to access protected resources. The token is sent in the Authorization header.
  • API Key Authentication: A simple method where a unique key is included in the request (e.g., header, query parameter) to identify the client. It’s lightweight but less secure than token-based methods.
  • Bearer Token Authentication: A method where a token (e.g., JWT) is sent in the Authorization header with the prefix Bearer. It’s commonly used with OAuth 2.0 or standalone tokens.

REST Assured supports these methods via flexible header and parameter configurations. For OAuth 2.0, we’ll use a simplified Client Credentials flow with a mock token endpoint. For API Key and Bearer Token, we’ll use https://httpbin.org endpoints to simulate authentication.

Setting Up for Authentication Testing

We’ll set up a Maven project with REST Assured, JUnit, and Allure for reporting, consistent with previous posts. No additional dependencies are needed for these authentication types.

Here’s the pom.xml, styled with your preferred Blogger format for XML syntax highlighting:


<project xmlns="http://maven.apache.org/POM/4.0.0">
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>rest-assured-tests</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <java.version>11</java.version>
        <allure.version>2.27.0</allure.version>
        <aspectj.version>1.9.22</aspectj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>5.4.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest</artifactId>
            <version>2.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit5</artifactId>
            <version>${allure.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.5</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"</argLine>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>2.12.0</version>
                <configuration>
                    <reportVersion>${allure.version}</reportVersion>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

For OAuth 2.0, we’ll simulate a token endpoint. For API Key and Bearer Token, we’ll use https://httpbin.org endpoints like /headers to verify headers. No additional setup is required.

Implementing OAuth 2.0 Authentication

OAuth 2.0 involves obtaining an access token and using it in subsequent requests. We’ll use the **Client Credentials** flow, which is simpler for API testing, with a mock token endpoint.

Example 1: OAuth 2.0 Client Credentials Flow

Obtain an access token and use it to access a protected endpoint.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("OAuth 2.0 Authentication Testing")
public class OAuth2ClientCredentialsTest {

    @Test
    @Description("Test OAuth 2.0 Client Credentials flow")
    public void testOAuth2ClientCredentials() {
        RestAssured.baseURI = "https://httpbin.org";

        // Simulate obtaining access token
        Response tokenResponse = given()
            .auth().preemptive().basic("client_id", "client_secret")
            .formParam("grant_type", "client_credentials")
            .contentType("application/x-www-form-urlencoded")
            .log().all()
        .when()
            .post("/post") // Mock token endpoint
        .then()
            .statusCode(200)
            .extract().response();

        // Mock token (in real scenario, extract from response)
        String accessToken = "mock_access_token";

        // Use token in protected request
        given()
            .header("Authorization", "Bearer " + accessToken)
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .body("headers.Authorization", equalTo("Bearer " + accessToken));
    }
}

Explanation:

  • auth().preemptive().basic("client_id", "client_secret"): Authenticates to the token endpoint (mocked here).
  • formParam("grant_type", "client_credentials"): Specifies the OAuth 2.0 grant type.
  • header("Authorization", "Bearer " + accessToken): Uses the token in a protected request.
  • In production, extract the token from the access_token field in the token response.
Important: For real OAuth 2.0 testing, configure client credentials and token endpoints per your API’s OAuth provider (e.g., Auth0, Okta).

Implementing API Key Authentication

API Key Authentication involves including a key in the request header or query parameter.

Example 2: API Key in Header

Test an endpoint using an API key in the request header.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("API Key Authentication Testing")
public class ApiKeyHeaderTest {

    @Test
    @Description("Test API Key Authentication in header")
    public void testApiKeyInHeader() {
        RestAssured.baseURI = "https://httpbin.org";

        given()
            .header("X-Api-Key", "my-api-key")
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .body("headers.X-Api-Key", equalTo("my-api-key"));
    }
}

Explanation:

  • header("X-Api-Key", "my-api-key"): Includes the API key in the request header.
  • body("headers.X-Api-Key", ...): Verifies the header was sent correctly.

Example 3: API Key in Query Parameter

Test an endpoint using an API key in the query parameter.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("API Key Authentication Testing")
public class ApiKeyQueryTest {

    @Test
    @Description("Test API Key Authentication in query parameter")
    public void testApiKeyInQuery() {
        RestAssured.baseURI = "https://httpbin.org";

        given()
            .queryParam("api_key", "my-api-key")
            .log().all()
        .when()
            .get("/get")
        .then()
            .statusCode(200)
            .body("args.api_key", equalTo("my-api-key"));
    }
}

Explanation:

  • queryParam("api_key", "my-api-key"): Includes the API key in the query string.
  • body("args.api_key", ...): Verifies the query parameter was sent.
Pro Tip: Prefer header-based API keys over query parameters for security, as query parameters may be logged in server logs.

Implementing Bearer Token Authentication

Bearer Token Authentication involves sending a token in the Authorization header with the Bearer prefix.

Example 4: Bearer Token Authentication

Test an endpoint using a Bearer token.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Bearer Token Authentication Testing")
public class BearerTokenTest {

    @Test
    @Description("Test Bearer Token Authentication")
    public void testBearerToken() {
        RestAssured.baseURI = "https://httpbin.org";

        String bearerToken = "my-bearer-token";

        given()
            .header("Authorization", "Bearer " + bearerToken)
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .body("headers.Authorization", equalTo("Bearer " + bearerToken));
    }
}

Explanation:

  • header("Authorization", "Bearer " + bearerToken): Sends the token in the Authorization header.
  • body("headers.Authorization", ...): Verifies the header was sent correctly.
  • In production, obtain the token from an OAuth 2.0 flow or another authentication mechanism.

Example 5: Bearer Token with Response Specification

Combine Bearer Token Authentication with a ResponseSpecification for reusable validation.


import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.specification.ResponseSpecification;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("Bearer Token Authentication Testing")
public class BearerTokenWithSpecTest {

    private ResponseSpecification authSuccessSpec;

    @BeforeEach
    public void setup() {
        authSuccessSpec = new ResponseSpecBuilder()
            .expectStatusCode(200)
            .expectContentType("application/json")
            .expectBody("headers.Authorization", containsString("Bearer"))
            .build();
    }

    @Test
    @Description("Test Bearer Token with Response Specification")
    public void testBearerTokenWithSpec() {
        RestAssured.baseURI = "https://httpbin.org";

        String bearerToken = "my-bearer-token";

        given()
            .header("Authorization", "Bearer " + bearerToken)
            .log().all()
        .when()
            .get("/headers")
        .then()
            .spec(authSuccessSpec)
            .body("headers.Authorization", equalTo("Bearer " + bearerToken));
    }
}

Explanation:

  • authSuccessSpec: Defines a specification for successful Bearer Token responses.
  • spec(authSuccessSpec): Applies the specification to validate the response.

Integrating with Allure Reporting

Document authentication tests with Allure, attaching request and response details.


import io.qameta.allure.Allure;
import io.qameta.allure.Description;
import io.qameta.allure.Feature;
import io.restassured.RestAssured;
import io.restassured.response.Response;
import org.junit.jupiter.api.Test;
import static io.restassured.RestAssured.*;
import static org.hamcrest.Matchers.*;

@Feature("OAuth 2.0, API Key, and Bearer Token Authentication Testing")
public class AllureAuthTest {

    @Test
    @Description("Test OAuth 2.0 with Allure reporting")
    public void testOAuth2WithAllure() {
        RestAssured.baseURI = "https://httpbin.org";

        Response tokenResponse = given()
            .auth().preemptive().basic("client_id", "client_secret")
            .formParam("grant_type", "client_credentials")
            .contentType("application/x-www-form-urlencoded")
            .log().all()
        .when()
            .post("/post")
        .then()
            .statusCode(200)
            .extract().response();

        String accessToken = "mock_access_token";

        Response protectedResponse = given()
            .header("Authorization", "Bearer " + accessToken)
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .extract().response();

        Allure.addAttachment("Token Response", "application/json", tokenResponse.asString(), ".json");
        Allure.addAttachment("Protected Response", "application/json", protectedResponse.asString(), ".json");
    }

    @Test
    @Description("Test API Key with Allure reporting")
    public void testApiKeyWithAllure() {
        RestAssured.baseURI = "https://httpbin.org";

        Response response = given()
            .header("X-Api-Key", "my-api-key")
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .extract().response();

        Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
    }

    @Test
    @Description("Test Bearer Token with Allure reporting")
    public void testBearerTokenWithAllure() {
        RestAssured.baseURI = "https://httpbin.org";

        String bearerToken = "my-bearer-token";

        Response response = given()
            .header("Authorization", "Bearer " + bearerToken)
            .log().all()
        .when()
            .get("/headers")
        .then()
            .statusCode(200)
            .extract().response();

        Allure.addAttachment("Response Body", "application/json", response.asString(), ".json");
    }
}

Explanation:

  • Allure.addAttachment: Attaches response bodies to the Allure report for each test.
  • Run mvn clean test and mvn allure:serve to view the report.

Integrating with CI/CD

Add authentication tests to a GitHub Actions pipeline for automation.

Create or update .github/workflows/ci.yml:


name: REST Assured CI Pipeline

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK 11
        uses: actions/setup-java@v4
        with:
          java-version: '11'
          distribution: 'temurin'

      - name: Cache Maven dependencies
        uses: actions/cache@v4
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
          restore-keys: ${{ runner.os }}-maven-

      - name: Run tests
        run: mvn clean test

      - name: Generate Allure report
        run: mvn allure:report

      - name: Upload Allure results
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: allure-results
          path: target/allure-results

      - name: Publish Allure report
        if: always()
        uses: simple-elf/allure-report-action@v1.7
        with:
          allure_results: target/allure-results
          gh_pages: gh-pages
          allure_report: allure-report

      - name: Deploy report to GitHub Pages
        if: always()
        uses: peaceiris/actions-gh-pages@v4
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: allure-report

Explanation:

  • Runs authentication tests with mvn clean test.
  • Publishes Allure reports to GitHub Pages for visibility.

Tips for Beginners

  • Secure Credentials: Store OAuth client IDs, API keys, and tokens in environment variables or secure vaults, not hardcoded in tests.
  • Understand OAuth Flows: Start with Client Credentials for machine-to-machine APIs; explore Authorization Code for user-based flows.
  • Validate Tokens: Check token expiration and refresh tokens as needed in production tests.
  • Log Selectively: Use log().ifValidationFails() to debug authentication failures efficiently.
Troubleshooting Tip: If authentication fails, enable log().all() to inspect headers (e.g., Authorization, X-Api-Key) and verify credentials against API documentation.

What’s Next?

This post enhances our REST Assured series by covering OAuth 2.0, API Key, and Bearer Token Authentication, essential for modern API testing. To continue your learning, consider exploring:

  • GraphQL Testing: Testing GraphQL APIs with REST Assured.
  • Advanced Allure Reporting: Customizing reports with environments and categories.
  • End-to-End Testing: Combining REST Assured with Selenium for UI and API testing.
Stay tuned for more testing tutorials!