Skip to main content

HTTP REQUESTS

Request Signing

Sign outgoing HTTP requests with HMAC, AWS SigV4, or any custom scheme. Karate evaluates a JavaScript function just before each request leaves the wire, giving the signer access to the final method, path, headers, and body — everything you need to compute a signature over a canonical string.

The Mechanism

Assign a function (not a map) to configure headers. The function is called once per request, receives the built HttpRequest, and returns the headers to add:

Gherkin
* configure headers =
"""
function(req) {
// compute headers using req.method, req.path, req.body, etc.
return { 'X-Signature': '...' }
}
"""

The returned map is merged into the request's headers (case-insensitive).

What's on req

PropertyTypeExample
req.methodString'POST'
req.url / req.urlAndPathString'https://api.example.com/v1/orders'
req.pathString'/v1/orders'
req.paramsMap{ region: ['us-east-1'] }
req.headersMap{ 'Content-Type': ['application/json'] }
req.bodybyte[]the raw bytes that will be sent (may be null for GET)
req.bodyStringStringUTF-8 view of req.body

The body is the final, fully-assembled body — including form fields, multipart parts, and JSON serialisation. The function runs after the request is built, so what you sign is what gets sent.

Example: HMAC-SHA256

A common pattern: sign a canonical string (METHOD\nPATH\nTIMESTAMP\nBODY) with HMAC-SHA256, send the result in an X-Signature header alongside the timestamp.

Gherkin
Feature: HMAC-signed API calls

Background:
* def secret = karate.sysenv('API_SECRET')
* def signRequest =
"""
function(req) {
var Mac = Java.type('javax.crypto.Mac')
var Spec = Java.type('javax.crypto.spec.SecretKeySpec')
var Base64 = Java.type('java.util.Base64')

var ts = '' + java.lang.System.currentTimeMillis()
var body = req.bodyString || ''
var canonical = req.method + '\n' + req.path + '\n' + ts + '\n' + body

var mac = Mac.getInstance('HmacSHA256')
mac.init(new Spec(secret.getBytes('UTF-8'), 'HmacSHA256'))
var sig = Base64.getEncoder().encodeToString(mac.doFinal(canonical.getBytes('UTF-8')))

return { 'X-Timestamp': ts, 'X-Signature': sig }
}
"""
* configure headers = signRequest
* url 'https://api.example.com'

Scenario: signed GET
Given path 'orders'
When method get
Then status 200

Scenario: signed POST with JSON body
Given path 'orders'
And request { item: 'widget', qty: 3 }
When method post
Then status 201

The same signRequest works for both GET (no body) and POST (with body) — req.method and req.body reflect whatever the scenario built.

AWS SigV4

Karate does not ship a built-in SigV4 signer. Rather than implement SigV4 in JavaScript (CanonicalRequest, StringToSign, HMAC chain, signed-headers list, X-Amz-Date format — each easy to get subtly wrong), call the AWS SDK for Java v2 from the headers function via Karate's Java interop.

Add the SDK dependency to your pom.xml:

pom.xml
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>auth</artifactId>
</dependency>

Then call Aws4Signer from the headers function with the same (req) => headersMap shape as the HMAC example above. References:

For short-lived pre-signed URLs (S3 object access, etc.) use the SDK's S3Presigner to produce a complete URL you can pass straight to url — no headers-function needed.

When Not to Use This

The headers function is for headers that genuinely depend on the request. For everything else, prefer the simpler tools:

NeedUse
Static headers for every requestconfigure headers = { ... } (map, not function)
Bearer token from a login callconfigure auth = { type: 'bearer', token: '#(...)' }
OAuth 2.0 client credentialsconfigure auth = { type: 'oauth2', ... }
Basic authconfigure auth = { type: 'basic', username: '...', password: '...' }
Per-call header overrideheader X-Foo = 'bar' on the request itself

Next Steps