Authenticate requests
Copy page
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.
Protect an inbox source
Section titled “Protect an inbox source”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:
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.
sources: partner: type: http path: /partner idempotencyKeyPath: $.id eventTypePath: $.type auth: type: api-key headerName: X-API-Key key: ${PARTNER_API_KEY}The sender puts the key in the header that headerName names. The default is X-API-Key.
curl -X POST http://localhost:8080/inbox/partner \ -H "X-API-Key: $PARTNER_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"id":"evt-1","type":"partner.updated"}'sources: github: type: http path: /github idempotencyKeyPath: $.delivery eventTypePath: $.action auth: type: hmac secret: ${GITHUB_WEBHOOK_SECRET} headerName: X-Hub-Signature-256 algorithm: HmacSHA256 signaturePrefix: "sha256="QueueBox computes the HMAC of the raw request body with secret. It writes the result as lower case hexadecimal after signaturePrefix, and compares it with the header. This matches the X-Hub-Signature-256 header that GitHub sends.
A sender computes the same value:
body='{"delivery":"d-1","action":"opened"}'signature="sha256=$(printf '%s' "$body" | openssl dgst -sha256 -hmac "$GITHUB_WEBHOOK_SECRET" | sed 's/^.* //')"curl -X POST http://localhost:8080/inbox/github \ -H "X-Hub-Signature-256: $signature" \ -H 'Content-Type: application/json' \ -d "$body"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.
HMAC settings
Section titled “HMAC settings”| 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. |
Reject replayed requests
Section titled “Reject replayed requests”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: 300000When 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.
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.
Protect the admin endpoint
Section titled “Protect the admin endpoint”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: 65536admin.auth takes the same bearer and api-key blocks as a source. A request without valid credentials gets 401.
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.
Authenticate to a destination
Section titled “Authenticate to a destination”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-apiQueueBox runs the client credentials flow against tokenUrl, and it sends the access token as a bearer token. scope and extraParams are optional.
destinations: legacy-api: type: http baseUrl: https://legacy.example.com auth: type: basic username: queuebox password: ${LEGACY_API_PASSWORD}
routes: - topicPattern: "**" destination: legacy-apidestinations: static-api: type: http baseUrl: https://static.example.com auth: type: header headerName: Authorization headerValue: "Bearer ${STATIC_TOKEN}"
routes: - topicPattern: "**" destination: static-apiheaderName defaults to Authorization.
A row header of the same name wins over the authentication header. Do not write credential headers into outbox rows.
Keep secrets out of the file
Section titled “Keep secrets out of the file”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:
# fragmentsources: orders: auth: type: bearer token: file:/var/run/secrets/queuebox/orders-tokenQueueBox 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.