In the previous post, we built our first full user flow with k6. Now it’s time to move closer to real production scenarios—where users authenticate, carry tokens, and send meaningful headers with each request.
In this post, we’ll:
- Log in using email/password
- Extract a JWT token from the response
- Store it for the rest of the test
- Call protected endpoints using Authorization: Bearer
- Add custom headers such as X-Client-Version and X-Request-Source
- Run a multi-step authenticated flow
This is the foundation for every real-world SaaS, e-commerce, dashboard, and API microservice load test.
Table of contents
Open Table of contents
Flow Overview (ASCII Diagram)
+-------------------+
| User Login |
| POST /auth/login |
+---------+---------+
|
| extract JWT
v
+---------------------------+
| Request Protected Resource|
| GET /my/crocodiles |
+-------------+-------------+
|
| send token + custom headers
v
+------------------------------+
| Update Item (authenticated) |
| PUT /my/crocodiles/<id> |
+------------------------------+
Over four weeks, you’ll move from basic scripting all the way to building a complete performance testing suite. Each post will build on the last with clear examples, walkthroughs, and exercises. By the end, you’ll be comfortable modeling realistic workloads, analyzing results, scaling tests across distributed systems, and integrating everything into automated pipelines.
The focus areas include:
- Core k6 concepts
- Script structure and modularization
- Test scenarios and workload modeling
- Metrics, thresholds, and analysis
- CI/CD integration
- Distributed and large-scale testing
- Extensions, observability, and dashboards
- Designing maintainable performance test suites
This post sets up everything you’ll need to start.
Environment Setup
Before diving deeper, install everything you need.
- Install k6 on your machine
Run:
sudo apt update
sudo apt install k6
or go to K6’s official installation guide and install based on your preferred package manager.
Verify:
k6 version
- Choose an Editor
VS Code works well due to its extensions for JavaScript and testing workflows.
- Create a Working Directory
This directory will store all blog-series scripts:
mkdir k6-course
cd k6-course
Code Walkthrough
Login payload and headers
const loginPayload = JSON.stringify({
username: "testuser@example.com",
password: "supersecure",
});
const loginHeaders = {
"Content-Type": "application/json",
"X-Client-Version": "1.0.0",
"X-Request-Source": "k6-learn-series",
};
loginPayloadis the JSON body sent to the login endpoint.loginHeadersinclude Content-Type and two custom headers. Custom headers help identify traffic or provide metadata to the server (client version, source, etc.).
POST /auth/token/login/ — retrieve JWT
const loginRes = http.post(
"https://fakeloadtest.com/auth/token/login/",
loginPayload,
{ headers: loginHeaders }
);
loginResSends credentials to the login endpoint.- The server should return an
access tokenin the response body (here we expect a JSON field named access).
Validate login and token presence with checks
check(loginRes, {
"login successful": (r) => r.status === 200,
"token received": (r) => r.json("access-token") !== undefined,
});
# Extract token and short-circuit if missing
const token = loginRes.json("access-token");
if (!token) return;
- Two checks:
HTTP 200for success, and that the JSONcontainsanaccess-tokenfield. - check results are visible in the test summary and help define whether the test is exercising healthy endpoints.
loginRes.json("access-token")reads the access field from the JSON response.- If there’s no token, return exits this VU iteration early (prevents further unauthorized calls).
Build auth headers
const withAuth = {
...commonHeaders,
Authorization: `Bearer ${token}`
};
- Combine the Auth headers with common headers. This way we can just send
withAuthheaders without duplicating common headers.
Putting it all together
import http from "k6/http";
import { sleep, check } from "k6";
export const options = {
vus: 5,
duration: "30s",
};
export default function () {
// --- shared/common headers used for all requests (non-auth + auth)
const commonHeaders = {
"Content-Type": "application/json",
"X-Client-Version": "1.0.0",
"X-Request-Source": "k6-learn-series",
};
// 1. Login and retrieve JWT
const loginPayload = JSON.stringify({
username: "testuser@example.com",
password: "supersecure",
});
const loginRes = http.post(
"https://fakeloadtest.com/auth/token/login/",
loginPayload,
{ headers: commonHeaders } // use common headers even for login
);
check(loginRes, {
"login successful": (r) => r.status === 200,
"token received": (r) => r.json("access") !== undefined,
});
const token = loginRes.json("access");
if (!token) return;
// Build withAuth by copying commonHeaders and adding Authorization
const withAuth = {
...commonHeaders,
Authorization: `Bearer ${token}`
};
// 2. Get Product list (protected endpoint)
let products = http.get("https://fakeloadtest.com/api/products/");
check(products, {
"Get Products": (r) => r.status === 200,
});
const list = products.json();
if (!list.length) return;
// 3. Update a product
const id = list[0].id;
const updatePayload = JSON.stringify({
name: "UpdatedTest",
qty: 5,
});
let updated = http.put(
`https://fakeloadtest.com/api/products/${id}/`,
updatePayload,
{ headers: withAuth }
);
check(updated, {
"update success": (r) => r.status === 200,
});
sleep(1);
}
k6-course/auth-flow.js
Now Run:
k6 run auth-flow.js
The script would run with 1 user as a default and give you some nice statistics like:
- request count
- duration metrics
- checks
- iterations
This confirms everything is installed and ready.
Exercises
Try these quick steps:
-
After updating the product, add
DELTEOperation:- http.del(…)
- Pass a custom header:
- X-Debug-Mode: “true”
-
Reuse the same authHeaders
-
Add a check to verify deletion succeeded
-
Add sleep(1) after the delete