Skip to content

This guide shows how to make QueueBox check the credentials of an incoming request, on an HTTP inbox source and on the admin endpoint. It also shows how QueueBox sends credentials to an HTTP destination.

An inbox source without auth accepts every request. A request that fails the check gets 401 Unauthorized, and QueueBox stores nothing.

Add an auth block to the source. Choose one of three types.

sources:
orders:
type: http
path: /orders
idempotencyKeyPath: $.id
eventTypePath: $.type
auth:
type: bearer
token: ${ORDERS_WEBHOOK_TOKEN}

The sender puts the token in the Authorization header:

Terminal window
curl -X POST http://localhost:8080/inbox/orders \
-H "Authorization: Bearer $ORDERS_WEBHOOK_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"id":"order-1","type":"order.created"}'

The scheme name Bearer matches in any letter case. A header without a scheme fails.

QueueBox compares every credential in constant time. A stored inbox row never holds the Authorization header, the Proxy-Authorization header, the Cookie header, or the header that auth.headerName names.

Field Default Meaning
secret none, required The shared secret.
headerName X-Signature The header that carries the signature.
algorithm HmacSHA256 HmacSHA256 or HmacSHA512. No other value loads.
signaturePrefix sha256= The text before the hexadecimal signature. Set "" for none.
timestampHeader none A header that carries the send time, in milliseconds since the Unix epoch.
timestampTolerance 300000 The largest difference, in milliseconds, between the send time and the clock of QueueBox.
signaturePayloadFormat see below body or timestamp-dot-body.

Without a timestamp, a captured request stays valid for ever. Set timestampHeader to make QueueBox reject an old request:

sources:
billing:
type: http
path: /billing
idempotencyKeyPath: $.id
eventTypePath: $.type
auth:
type: hmac
secret: ${BILLING_WEBHOOK_SECRET}
headerName: X-Signature
signaturePrefix: "sha256="
timestampHeader: X-Timestamp
timestampTolerance: 300000

When timestampHeader is set, signaturePayloadFormat defaults to timestamp-dot-body. The sender then signs the timestamp, a dot, and the body. A request with a fresh timestamp and an old signature fails.

Terminal window
body='{"id":"inv-1","type":"invoice.paid"}'
timestamp=$(( $(date +%s) * 1000 ))
signature="sha256=$(printf '%s.%s' "$timestamp" "$body" | openssl dgst -sha256 -hmac "$BILLING_WEBHOOK_SECRET" | sed 's/^.* //')"
curl -X POST http://localhost:8080/inbox/billing \
-H "X-Timestamp: $timestamp" \
-H "X-Signature: $signature" \
-H 'Content-Type: application/json' \
-d "$body"

Set signaturePayloadFormat: body only for a sender that signs the body alone. QueueBox then checks the age of the timestamp, but a replay with a new timestamp header passes. timestamp-dot-body without a timestampHeader does not load.

The admin endpoint runs a JSONata expression that the caller sends. QueueBox registers it only when admin.enabled is true, and it refuses to start when admin.auth is absent.

admin:
enabled: true
auth:
type: bearer
token: ${ADMIN_TOKEN}
maxTransformTimeoutMs: 1000
maxPayloadBytes: 65536

admin.auth takes the same bearer and api-key blocks as a source. A request without valid credentials gets 401.

Terminal window
curl -X POST http://localhost:8080/admin/transform/test \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"expression":"$.amount * 2","payload":{"amount":21}}'

The admin endpoint also accepts hmac. Sign the raw request body in the same way as for a source. QueueBox 0.4.0 and earlier answer 500 to every HMAC request on the admin endpoint, so use bearer or api-key there on those versions.

admin.insecure: true allows the endpoint with no authentication. Use it on a local machine only.

Set server.managementPort to move /admin, /health and /metrics to a separate port, and keep that port on an internal network. See Security.

An HTTP destination can send credentials on every request.

destinations:
protected-api:
type: http
baseUrl: https://api.example.com
path: /events
auth:
type: oauth2
clientId: queuebox
clientSecret: ${CLIENT_SECRET}
tokenUrl: https://auth.example.com/oauth/token
scope: api:write
extraParams:
audience: https://api.example.com
routes:
- topicPattern: "**"
destination: protected-api

QueueBox runs the client credentials flow against tokenUrl, and it sends the access token as a bearer token. scope and extraParams are optional.

A row header of the same name wins over the authentication header. Do not write credential headers into outbox rows.

Every secret field accepts an environment variable, such as ${ADMIN_TOKEN}, or a file: reference. A file: reference reads a mounted file, for example a Kubernetes secret, once at startup:

# fragment
sources:
orders:
auth:
type: bearer
token: file:/var/run/secrets/queuebox/orders-token

QueueBox prints a mask in place of every secret. database.url, the RabbitMQ destination url and the RabbitMQ source connectionUrl take no file: reference, because a log needs their host and port. QueueBox masks the password inside them. Set those through environment variables.