Headers and Authentication in Cucumber with REST Assured
What Are Headers and Authentication?
In API testing, headers provide additional information about the HTTP request or response, while authentication verifies the identity of the client making the request. Headers tell the server how to process the request, what format the client is sending, what format the client expects back, whether caching rules should apply, what language is preferred, and sometimes which client application is calling the API. Authentication tells the server who is making the request and whether that client has permission to access a protected resource.
Most modern REST APIs require both. A public health-check endpoint may not need authentication, but it will still return response headers. A secured customer endpoint usually needs an Authorization header, an Accept header, and sometimes a Content-Type header if the request contains a body. Without these details, the API may reject the request, return the wrong response format, or treat the client as unauthenticated.
In a Cucumber and REST Assured framework, headers and authentication should be handled carefully. The feature file should describe the behavior at a readable level, such as a valid client requesting customer details or an unauthenticated client being rejected. REST Assured should perform the technical work of adding headers, sending tokens, applying authentication methods, and validating response headers. This separation keeps Gherkin clean while still testing the real HTTP contract.
Why Headers Are Important
Headers are metadata. They do not usually represent the main business payload, but they strongly influence how the request is processed. A JSON POST request without the correct Content-Type header may fail because the server does not know how to parse the body. A request with an incorrect Accept header may receive XML or plain text when the automation expects JSON. A request without an Authorization header may receive 401 Unauthorized even when the endpoint path and body are correct.
Headers also support operational behavior. Correlation IDs and trace IDs help connect API calls across distributed systems. Cache-Control headers influence caching behavior. User-Agent can identify the caller. Accept-Language can affect localized response messages. Cookies may carry session state. Security headers in responses can protect clients and browsers. In real projects, headers are not optional decoration; they are part of the API contract.
HTTP Request
URL
HTTP Method
Headers
Authentication
Request Body
A good API automation framework treats headers as first-class request and response details. It centralizes common request headers, validates important response headers, and keeps sensitive authentication data secure. It also avoids repeating the same header setup in every step definition.
Common HTTP Headers
Several headers appear frequently in API automation. The Authorization header sends credentials or tokens. Content-Type specifies the format of the request body, such as application/json. Accept specifies the response format the client expects. User-Agent identifies the client application. Cache-Control controls caching behavior. Accept-Language specifies language preference. Cookie sends session information. Host identifies the target server.
| Header | Purpose |
|---|---|
| Authorization | Sends authentication credentials or tokens |
| Content-Type | Specifies the request body format |
| Accept | Specifies the expected response format |
| User-Agent | Identifies the client application |
| Cache-Control | Controls caching behavior |
| Accept-Language | Specifies preferred language |
| Cookie | Sends session information |
| Host | Identifies the target server |
Not every scenario needs every header. The framework should define sensible defaults and allow scenario-specific overrides when needed. For example, most JSON APIs can use a common Content-Type and Accept setup. A negative test may intentionally omit a header to verify that the API rejects the request correctly.
Request Flow with Headers and Authentication
The request flow begins with the client preparing an HTTP request. The request includes the URL, method, headers, authentication details, and body when needed. The server reads these details before deciding how to process the request. If authentication is valid and the request format is acceptable, the server processes the business operation and returns a response. If authentication is missing or invalid, the server may reject the request before business logic is executed.
Client
-> HTTP Request
-> Headers
-> Authentication
-> API Server
-> HTTP Response
This order is important for testing. If a protected endpoint returns 401, the business payload may not be evaluated at all. If a request has the wrong Content-Type, the request body may not be parsed. If the token is expired, the server may reject the request even though all other data is correct. Good troubleshooting starts by checking headers and authentication before assuming there is a business defect.
Adding Headers in REST Assured
REST Assured makes it easy to add request headers. The header() method can be chained inside the given() section before the request is sent. This is useful for examples, but in a real framework, repeated headers should usually be placed in a reusable request specification.
Response response =
given()
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.when()
.get("/users");
Multiple headers can be added in the same request. REST Assured also provides convenience methods such as contentType() and accept(), which are often cleaner than manually writing the header names.
given()
.header("Content-Type", "application/json")
.header("Accept", "application/json")
.header("User-Agent", "Automation Framework")
.header("Cache-Control", "no-cache");
For small scripts, this style is acceptable. For a scalable Cucumber framework, centralize common headers so every API call uses consistent configuration. This reduces copy-paste errors and makes later changes easier.
Content-Type Header
The Content-Type header indicates the format of the request body. If the client sends JSON, the request should usually include Content-Type: application/json. If it sends XML, form data, or multipart content, the Content-Type should match that format. The server uses this header to decide how to parse the body.
Content-Type: application/json
REST Assured provides a direct method for setting JSON content type.
given()
.contentType(ContentType.JSON);
This is equivalent to adding the Content-Type header manually, but it is clearer and less error-prone. Missing Content-Type is a common reason for POST and PUT request failures. If a request body looks correct but the API returns a parsing or unsupported media type error, Content-Type should be checked early.
Accept Header
The Accept header tells the server what response format the client expects. For JSON APIs, this is usually Accept: application/json. Some APIs can return JSON, XML, plain text, or other formats depending on the Accept header. If automation expects JSON and receives another format, parsing and validation may fail.
Accept: application/json
REST Assured provides a convenience method for setting the Accept header.
given()
.accept(ContentType.JSON);
In many frameworks, Accept and Content-Type are part of the default request specification. A scenario can override them only when testing format negotiation, unsupported formats, or content negotiation behavior. Keeping defaults centralized makes request behavior predictable.
Authorization Header
The Authorization header is one of the most important request headers in secured API testing. Many modern APIs use bearer tokens, where the header looks like Authorization: Bearer tokenValue. The token proves that the client has already authenticated and may access protected endpoints based on permissions and scope.
Authorization: Bearer eyJhbGciOi...
REST Assured can add this header manually.
given()
.header("Authorization", "Bearer " + token);
It can also use built-in authentication support for OAuth-style bearer tokens.
given()
.auth()
.oauth2(token);
Both approaches can work. Using REST Assured authentication methods is often cleaner when supported. Manual headers are still useful when the API uses a custom authorization format. The key rule is to centralize the logic and avoid hardcoding token strings in feature files or step definitions.
What Is Authentication?
Authentication verifies the identity of the client. It answers the question, "Who is making this request?" Authorization is related but different. Authorization answers, "What is this authenticated client allowed to do?" In API testing, both concepts often appear together. A client may be authenticated but still forbidden from accessing a specific resource.
Client
-> Authentication
-> API
-> Access granted or rejected
Without valid authentication, protected APIs typically return 401 Unauthorized. If the client is authenticated but lacks permission, the API may return 403 Forbidden. These differences matter. A good test suite should verify both invalid identity and insufficient permission scenarios.
Types of Authentication
REST APIs use different authentication mechanisms depending on security requirements, age of the system, and architecture. Common mechanisms include no authentication, Basic Authentication, Digest Authentication, bearer token authentication, OAuth 2.0, API key authentication, and cookie-based authentication. A mature framework may need to support more than one mechanism if the application integrates with multiple services.
Authentication
No Authentication
Basic Authentication
Digest Authentication
Bearer Token
OAuth 2.0
API Key
Cookie-Based Authentication
The feature file should not usually describe every technical detail of the authentication mechanism. It can say that a valid client, invalid client, expired token, or unauthorized role is used. The authentication service and request specification can handle the technical setup behind the scenes.
Basic Authentication
Basic Authentication uses a username and password encoded into an Authorization header. It is simple and still appears in internal APIs, legacy systems, tools, and some test environments. The header format is based on Base64 encoding of username and password. It is easy to use, but it should be protected by HTTPS because Base64 is encoding, not encryption.
Authorization: Basic Base64(username:password)
REST Assured supports preemptive basic authentication.
given()
.auth()
.preemptive()
.basic("admin", "password");
Credentials should not be hardcoded in Java code or feature files. Read them from secure configuration, environment variables, or secret-management systems. Even in test automation, credential exposure is a real risk.
Digest Authentication
Digest Authentication is used by some older or legacy APIs. It is more complex than Basic Authentication because it uses a challenge-response mechanism. REST Assured can handle digest authentication without the automation engineer manually building the header.
given()
.auth()
.digest("admin", "password");
Digest Authentication is less common in modern REST API projects, but it may still appear in enterprise environments. The main framework principle remains the same: keep credentials externalized, centralize authentication logic, and keep feature files focused on behavior.
Bearer Token Authentication
Bearer token authentication is very common in modern REST APIs. The client first authenticates through a login or token endpoint. The API returns an access token. The client sends that token in the Authorization header for protected endpoints. The server validates the token and decides whether access is allowed.
Authorization: Bearer tokenValue
REST Assured can send bearer tokens through OAuth2 support.
given()
.auth()
.oauth2(token);
Bearer tokens should be treated as sensitive values. Do not print them unnecessarily in logs. Do not store them in feature files. Do not paste long-lived tokens into source code. Generate or retrieve tokens dynamically where possible, store them in scenario context or controlled runtime storage, and mask them in reports.
OAuth 2.0
OAuth 2.0 is a broad authorization framework used by many modern applications. In test automation, the most common practical pattern is obtaining an access token and sending it as a bearer token to protected APIs. The full OAuth flow may involve client credentials, authorization code, refresh tokens, scopes, and identity providers depending on the application.
Login or token request
-> Access token
-> Protected API request
-> Response
A Cucumber scenario should focus on the behavior. For example, "Given a valid access token" or "Given an expired access token" is usually enough. The Java authentication service can know how to call the token endpoint, pass client credentials, parse the token, and store it. This prevents OAuth mechanics from polluting Gherkin.
API Key Authentication
Some APIs use API keys. An API key may be sent as a header, such as x-api-key, or sometimes as a query parameter. Header-based API keys are generally cleaner because they keep credentials out of URLs. REST Assured can add the key as a header.
given()
.header("x-api-key", apiKey);
API keys should be stored securely. They should not appear in feature files, screenshots, logs, or public repositories. If the same framework runs in multiple environments, each environment should have its own key managed through configuration or secrets.
API key tests should include both success and failure behavior. A valid key should allow access. A missing key, invalid key, or revoked key should be rejected with the expected status code and error message.
Cookie-Based Authentication
Cookie-based authentication is common in web applications and some APIs that use server-side sessions. After login, the server may return a session cookie. The client sends that cookie in later requests so the server can identify the session. REST Assured supports sending and receiving cookies.
given()
.cookie("SESSIONID", sessionId);
Cookie-based authentication may be relevant when API tests interact with systems that are tightly coupled to browser sessions or older authentication models. As with tokens, cookies should be handled carefully, stored only for the needed scope, and not exposed in reports unless safely masked.
Generating Tokens
Token generation is a common setup activity in API automation. A typical flow calls a login API, receives a token, stores it, and reuses it for protected API calls. Generating a token in every step definition is inefficient and creates duplication. Token logic should be centralized in an authentication service.
Login API
-> Receive token
-> Store token
-> Reuse token
-> Protected APIs
The authentication service can decide whether to generate a new token for every scenario, reuse a token for a test run, refresh a token when expired, or create different tokens for different roles. The choice depends on application behavior and test isolation needs. The important point is that feature files and step definitions should not duplicate token-generation mechanics.
Scenario Context for Tokens
Scenario context is useful for storing tokens and related authentication data during a scenario. A Given step may generate a token and store it in context. Later When steps can retrieve the token and call protected APIs. This keeps data available across steps without relying on unsafe global variables.
context.setToken(token);
String token = context.getToken();
Context should be scenario-scoped. Static shared tokens can cause problems in parallel execution, especially when different scenarios use different users or roles. Scenario-scoped storage keeps each scenario independent and easier to debug.
Centralizing Authentication
Centralized authentication is a major framework design practice. Instead of each API client generating tokens differently, create an AuthenticationService or AuthClient. This class handles login requests, token parsing, refresh behavior, and error handling. Other API clients can request the token through a common interface.
Authentication Service
-> Generate token
-> Return token
-> Store in scenario context
-> Reuse across API clients
Centralization reduces duplication and improves security. If the login endpoint changes, update one service. If token format changes, update one parser. If masking logic is needed, add it centrally. This design also makes it easier to test role-based scenarios because token generation can be controlled by user type, role, or scope.
Reusable Request Specification
A reusable RequestSpecification is the cleanest way to apply common headers and authentication in REST Assured. It can include base URI, content type, accept header, authorization token, default logging, and common filters. API client methods can start from this base specification and add endpoint-specific details.
RequestSpecification request =
given()
.baseUri(baseUrl)
.contentType(ContentType.JSON)
.accept(ContentType.JSON)
.header("Authorization", "Bearer " + token);
This avoids repeated setup. It also helps enforce consistency. If every request uses the same factory method, the framework can apply logging, masking, tracing, and default headers in one place. Negative tests can still override or remove headers when they intentionally test missing or invalid authentication.
Header Validation
Response headers can be validated with REST Assured. Content-Type validation is common because it confirms that the API returned the expected format. Other headers may also matter, such as cache-control, security headers, correlation IDs, rate-limit headers, or pagination metadata.
response.then()
.header("Content-Type", containsString("application/json"));
Header validation should be purposeful. If a scenario is about successful customer retrieval, Content-Type may be enough. If a scenario is about rate limiting, rate-limit headers become central. If a scenario is about tracing, correlation ID validation matters. Avoid adding low-value header assertions everywhere because they can create noisy failures.
Authentication Failure Scenarios
Authentication failure testing is necessary for secured APIs. Invalid credentials, missing token, expired token, malformed token, revoked token, missing API key, invalid API key, and insufficient role should all be considered where relevant. These tests prove that the API protects data and rejects unauthorized access predictably.
Scenario: Access protected API with invalid token
Given an invalid access token
When the client requests customer details
Then the response status code should be 401
Good negative authentication scenarios validate more than the status code when the API contract defines an error body. They may check an error code, message, timestamp, or trace ID. The feature file can describe the behavior clearly, while the validator performs technical checks.
Authorization and Role-Based Access
Authorization is not the same as authentication. A user may be authenticated but still not allowed to perform an action. For example, a support user may view customer details but not approve refunds. An employee may create a request but not approve their own request. A read-only API key may retrieve data but not update it.
Feature files should capture these rules in business language. "A support user cannot approve refunds" is clearer than "Token with SUPPORT role gets 403 on POST /refunds/approve." The step definition can use the correct token and the validator can check 403. The scenario communicates the rule.
Role-based access tests are important because authorization defects can be serious. They often expose data or allow actions that should be restricted. API automation should include both allowed and forbidden role scenarios for critical resources.
Configuration and Secrets
Base URLs, usernames, passwords, API keys, client IDs, client secrets, and token endpoints should not be hardcoded in feature files or Java classes. They should be externalized through configuration files, environment variables, CI/CD secrets, or secret-management systems. This allows the same framework to run safely across environments.
baseUrl=https://api.example.com
username=admin
password=secret
apiKey=xyz123
Even when values are stored in configuration, sensitive data should be handled carefully. Avoid printing secrets in logs. Avoid including them in reports. Avoid committing real credentials to source control. For local development, use safe test credentials and keep real values outside the repository.
Logging Headers Safely
REST Assured can log request and response headers, which is very helpful during debugging. Header logs can reveal missing authorization, wrong content type, incorrect API key names, and unexpected response formats. During development, logging headers may quickly identify why a request failed.
given()
.log().headers();
response.then()
.log().headers();
However, header logging can expose secrets. Authorization, Cookie, x-api-key, client-secret, and similar headers should be masked before reports are shared. Enterprise frameworks often implement filters that replace sensitive values with placeholders. Debugging should not create a security leak.
Cucumber Integration
Cucumber feature files should express authentication behavior without exposing implementation details. A scenario can say that a valid access token exists, an invalid access token is used, or an unauthenticated client requests data. The step definition maps that statement to the authentication service and REST Assured request setup.
Scenario: Get customer details
Given a valid access token
When the client requests customer information
Then the response status code should be 200
@Given("a valid access token")
public void aValidAccessToken() {
String token = authService.generateToken();
context.setToken(token);
}
This design keeps authentication implementation outside the feature file. It also makes the step reusable. Any scenario that needs a valid token can use the same Given step. The token generation details can change without changing the Gherkin.
Common Mistakes
A common mistake is hardcoding tokens directly in step definitions or feature files. Tokens expire and may expose sensitive access. Generate tokens dynamically or retrieve them securely. Another mistake is generating a new token in every API method even when one scenario-scoped token would be enough. This slows execution and creates unnecessary load on the authentication service.
Teams also expose passwords in source code, forget the Content-Type header when sending JSON, validate only the status code, or ignore response headers completely. Some frameworks duplicate authentication code in every API class. This creates inconsistent behavior and makes maintenance expensive.
Another mistake is treating all authentication failures the same. Missing token, invalid token, expired token, and insufficient permission may have different expected responses. A strong suite validates these differences when the API contract defines them.
Best Practices
Centralize authentication logic in a dedicated service. Store tokens in scenario context or another controlled runtime store. Reuse Request Specifications with common headers. Externalize credentials, API keys, token endpoints, and URLs. Use REST Assured authentication methods instead of manually constructing headers when they fit the API design.
Validate important response headers. Test both successful and failed authentication scenarios. Avoid exposing sensitive credentials in source code, feature files, logs, screenshots, or reports. Keep Cucumber steps business-focused and let the API layer handle request details.
Also create clear naming conventions for roles and users. For example, use terms such as valid customer, admin user, support user, read-only client, expired token, and invalid API key. These names make scenarios readable and reduce confusion during reviews.
Enterprise Framework Architecture
In an enterprise Cucumber and REST Assured framework, header and authentication handling should be modular. Feature files describe the behavior. Step definitions call authentication or API services. The AuthenticationService generates or retrieves tokens. Request builders prepare payloads. Request specifications apply common headers. REST Assured sends requests. Validators check response body and headers. Reports show the result.
Feature File
-> Step Definition
-> Authentication Service
-> Request Builder
-> REST Assured
-> REST API
-> Response
-> Validator
-> Report
This structure keeps authentication and header management centralized and reusable. It also supports growth. When a new API module is added, it can reuse the same authentication service and request specification. When a token format changes, the change is handled centrally.
Headers vs Authentication
Headers and authentication are closely related, but they are not the same. Headers carry request and response metadata. Authentication verifies client identity. Authentication is often sent through a header, especially the Authorization header, but headers can do many other things that are unrelated to identity.
| Headers | Authentication |
|---|---|
| Carry request metadata | Verify client identity |
| May include Content-Type, Accept, Cache-Control | May use Basic, Bearer, OAuth, API Key, or Cookie |
| Present in most HTTP requests | Required mainly for secured endpoints |
| Can be request or response headers | Usually sent as part of the request |
Understanding this distinction helps in interviews and real debugging. If a request fails with unsupported media type, the issue may be Content-Type. If it fails with 401, the issue may be authentication. If it fails with 403, the issue may be authorization. Each category points to a different troubleshooting path.
Real-Time Example
Imagine a customer API where only authenticated users can view customer records. The scenario says that a valid support user requests customer details and receives the customer information. The step definition generates a support-user token through AuthService, stores it in scenario context, and calls CustomerApi. CustomerApi builds a request specification with base URL, Accept header, Content-Type header, and Authorization header. REST Assured sends the request and returns the response.
The validator checks status code 200, Content-Type, customer ID, customer name, and required response fields. Another scenario uses an expired token and expects 401. Another scenario uses a read-only role for an update operation and expects 403. These scenarios clearly document security behavior while the Java framework handles token generation, headers, and validation details.
Testing Missing and Invalid Headers
Header testing should include more than the successful request path. APIs should behave predictably when required headers are missing, invalid, duplicated, or unsupported. For example, a JSON endpoint may reject a request without Content-Type. A versioned API may reject an unsupported version header. A protected endpoint may reject a request without Authorization. These are not minor technical checks; they are part of the API contract.
Negative header scenarios should be written with clear intent. A scenario can say that the API rejects a customer creation request when the request format is not specified. The Java layer can omit Content-Type and verify the correct status code and error message. This keeps the feature file readable while still validating the exact HTTP behavior. For teams that build public APIs, this kind of validation is important because client applications depend on consistent error responses.
Token Expiry and Refresh Handling
Token expiry is a real-world issue in API automation. Access tokens usually have a limited lifetime. If a long-running regression suite uses a token after it expires, protected requests may suddenly fail with 401. A strong authentication service should understand token expiry and either generate a fresh token for each scenario or refresh the token when needed. The right approach depends on the application and execution strategy.
For isolated scenarios, generating a token during setup is often simple and reliable. For large suites, token reuse may improve speed, but it must be handled safely. The framework should avoid sharing one mutable token across parallel scenarios if different roles are involved. It should also avoid hiding token-refresh failures. If the login API is down or token generation fails, the report should make that setup failure clear instead of showing confusing downstream API failures.
Role-Based Token Management
Many enterprise applications use role-based access. An admin user, customer user, support user, finance user, and read-only user may all receive valid tokens, but each token allows different actions. A good framework should make role-based token generation easy. The feature file can use readable steps such as "Given a support user is authenticated" or "Given an admin client is authenticated." The authentication service can generate the correct token behind the step.
This design avoids hardcoding role details into every scenario. It also makes authorization coverage easier to review. When reports show failures for admin, support, or read-only scenarios, the team can quickly understand which access rule is affected. Role-based token management is especially important for security-sensitive APIs because permission defects can expose data or allow restricted actions.
Masking Sensitive Values in Reports
API reports are useful only when they can be shared safely. Headers often contain sensitive data such as bearer tokens, API keys, cookies, session IDs, client secrets, and authorization signatures. If a framework attaches raw request and response details to reports, it may accidentally expose credentials. This is risky in local logs, CI artifacts, screenshots, and downloaded reports.
A mature framework masks sensitive headers before logging or attaching them to reports. For example, Authorization can be displayed as Bearer ****, cookies can be shortened, and API keys can show only a small prefix or no value at all. Masking should be centralized so every log path follows the same rule. This protects the team while still leaving enough diagnostic information to debug failures.
Troubleshooting Authentication Failures
When an authenticated API request fails, troubleshoot in layers. First confirm that the request reached the correct environment and endpoint. Then check whether the Authorization header or API key was actually sent. Next verify whether the token is valid, expired, malformed, or generated for the wrong role. After that, check whether the endpoint requires a different scope or permission. This structured approach prevents random changes to tests.
Status codes provide clues. A 401 response usually points to missing or invalid identity. A 403 response usually means the identity is known but not allowed. A 415 response may point to Content-Type. A 406 response may point to Accept. A 500 response after authentication succeeds may indicate a server-side defect or invalid downstream dependency. Reading headers, body, and logs together gives a much clearer picture than looking only at the status code.
Interview-Ready Summary
Headers provide metadata that helps the server process HTTP requests and helps the client interpret HTTP responses. Authentication verifies the identity of the client and controls access to protected resources. REST Assured supports multiple authentication mechanisms, including Basic Authentication, Digest Authentication, OAuth 2.0 bearer tokens, API keys, and cookie-based authentication.
In enterprise frameworks, authentication logic should be centralized, credentials should be externalized, request specifications should be reused, and tokens should be stored in scenario context. Good API automation validates request and response headers in addition to business functionality. It also tests both successful authentication and failure cases such as invalid tokens, expired tokens, missing API keys, and insufficient permissions.
Golden Rules
Keep authentication logic centralized in an AuthenticationService. Store tokens securely and reuse them through scenario context when appropriate. Externalize credentials, API keys, token endpoints, and URLs. Use REST Assured's built-in authentication methods whenever they fit the API. Validate headers, authentication behavior, authorization rules, and business responses, not just the HTTP status code.
The practical takeaway is simple: headers tell the API how to handle the request, authentication tells the API who is making the request, and a well-designed Cucumber REST Assured framework keeps both concerns reusable, secure, and readable.