OAuth 2.0 Authentication With Gatling Using Bearer Token

This post explains how to do Oauth2 Authentication with Gatling.

In this example, we are sending a request to create a user. However, the user endpoint is protected and requires an access_token.

First, we will get a bearer_token or an access_token and then send it as a header to the next API request to create a user.

To illustrate this, we will be using the same project structure for Gatling that we built previously:

Performance Testing Framework with Gatling and Maven

When we follow the steps in the above post, we would have our project structure as follows:

Defining Parameters in Configuration

First we define our OAuth 2.0 parameters in the Configuration.scala object file under the config folder:

object Configuration {
  val environment: String = System.getProperty("environment")
  val clientId: String = System.getProperty("CLIENT_ID")
  val clientSecret: String = System.getProperty("CLIENT_SECRET")
  val apiURL: String = "https://some-sub-domain." + environment + "some-domain.com/api"
  var tokenPath: String = "https://some-sub-domain" + environment + ".eu.auth0.com/oauth/token"
  val userPath = "/identity/iaa/v1/users"
}

Requests

Now we need to write the code that sends the request to the authorization server to get a bearer token.

OAuth 2.0 Request - access_token

This file AuthRequest.scala is saved under the requests folder in our project structure.

import java.io.{BufferedWriter, FileWriter}

import config.Configuration
import io.gatling.core.Predef._
import io.gatling.http.Predef._

object AuthRequest {

  val getAccessToken = exec(http("Get access token")
    .post(Configuration.tokenPath)
    .body(StringBody(
      s"""{
          "client_id": "${Configuration.clientId}",
          "client_secret": "${Configuration.clientSecret}",
          "audience": "https://some-domain-name.com/user",
          "grant_type": "client_credentials",
          "scope": "user:admin"
        }"""
    ))
    .asJson
    .headers(Map("Content-Type" -> "application/json"))
    .check(status.is(200))
    .check(jsonPath("$.access_token").saveAs("access_token")))
    .exec {
      session =>
        val fw = new BufferedWriter(new FileWriter("access_token.txt", true))
        try {
          fw.write(session("access_token").as[String] + "\r\n")
        }
        finally fw.close()
        session
    }
}

In the above code snippet, we are also saving the access_token to a file.

The above call, just gets the access_token.

We need another request to create a user by sending the access_token as the header.

User Request

Our user request is in a file called UserRequests.scala and is saved under the requests folder.

import config.Configuration.{apiURL, userPath}
import io.gatling.core.Predef._
import io.gatling.http.Predef._

object UserRequests {

  private val auth0Headers = Map(
    "Accept" -> "application/json, text/javascript, */*; q=0.01",
    "Content-Type" -> "application/json",
    "Authorization" -> "Bearer ${access_token}")

  val createUser = exec(http("Create user")
    .post(apiURL + userPath)
    .headers(auth0Headers)
    .body(ElFileBody("createUser.json"))
    .check(status.is(201)))
}

Scenario

Now we write out scenario object. In this example our object is called UserScenarios.scala and is saved under the scenario folder.

import requests.{AuthRequest, UserRequests}
import io.gatling.core.Predef._

object UserScenarios {

  var userIds:Array[Map[String,String]] =
    (100 to 900).toArray map ( x => { Map( "userId" -> x.toString) })

  val getAccessToken = scenario("Get token")
    .exec(AuthRequest.getAccessToken)

  val createUserScenario = scenario("Create user")
    .feed(userIds.circular)
    .exec(UserAuthZRequest.getAccessToken)
    .exec(UserRequests.createUser)
}

The above request, sends a POST request to create a user with the access_token as a bearer in the header.

Simulation

Finally our simulation file called UserSimulation.scala is saved under the simulations folder.

import scenario.UserScenarios
import io.gatling.core.Predef._
import scala.concurrent.duration._

class UserSimulation extends Simulation {
  setUp(
    UserScenarios.createUserScenario.inject(rampUsers(250) during (15 minutes)),
  )
}

To run the tests we use

mvn clean gatling:test