Skip to content

Cryptographic mediation

A bearer-token swap is the easy half of credential mediation. The other half is the family of upstream authentication shapes where the proxy must sign the outbound request — a new artifact computed at request time from a private key that the workload must never see.

Excalibur ships first-class mediation for four such families:

Family What the proxy does Common upstreams
JWT Bearer Mints and signs an RFC 7523 JWT assertion per call, exchanges it for an upstream access token GitHub Apps, Google service accounts, Salesforce
DPoP Holds the DPoP private key, generates a fresh DPoP proof bound to each request URL + method Modern OAuth deployments with sender-constrained tokens
Upstream mTLS Holds the client certificate and private key, performs the TLS handshake to the upstream Bank APIs, healthcare, regulated SaaS
AWS SigV4 Holds the IAM access key, signs the canonical request including streaming body and trailer checksums Every AWS API: S3, STS, EventStream, presigned URLs

The workload implements none of these. It sends plain HTTP with a placeholder. The proxy does the cryptography upstream.


Where in the UI

Crypto Mediation.

Crypto mediation tabs

A four-tab panel — JWT Bearer, DPoP, mTLS, SigV4 — each backed by its own mappings table:

Column Meaning
ID Mapping ID
Provider The provider this mapping is bound to
Authority Target authority (relevant for mTLS, where it matches the SNI)
Token host Where token-exchange POSTs go (JWT-Bearer, DPoP)
Resource host Where the resulting access token will be presented
Path prefix Path-prefix scope
Surrogate The opaque artifact the proxy presents for restoration tracking

Reads are operator-level. Writes are admin-only via the API.


1. JWT Bearer

A JWT-Bearer mapping tells the proxy how to mint a self-signed JWT (RFC 7523), POST it to the upstream's token endpoint, and use the returned access token for the actual API call.

Create a mapping

curl -X POST http://localhost:8443/api/crypto/jwt-bearer/mappings \
  -H "Content-Type: application/json" \
  -d '{
        "provider_id":      "github-app-prod",
        "token_host":       "api.github.com",
        "path_prefix":      "/app",
        "client_id":        "Iv23li-your-github-app-id",
        "issuer":            "your-issuer",
        "subject":          "your-subject",
        "audience":         "https://api.github.com",
        "real_private_pem": "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----"
      }'

The proxy holds the private PEM. The workload knows nothing about the JWT. From the workload's view it is just an HTTPS call.

Workload usage

curl https://api.github.com/app/installations

(No Authorization header needed — the proxy injects the freshly exchanged token.)

What gets recorded

Event Details
jwt_bearer_mapping_created Provider, surrogate kid
jwt_bearer_signed Per-call: issuer, audience, kid
jwt_bearer_exchange Per-call: token endpoint, status

2. DPoP

DPoP (RFC 9449) sender-constrains an access token by binding it to a per-request signed proof. Excalibur holds the proof key, signs a fresh proof per request, and forwards both proof and access token upstream.

Create a mapping

curl -X POST http://localhost:8443/api/crypto/dpop/mappings \
  -H "Content-Type: application/json" \
  -d '{
        "provider_id":          "internal-idp",
        "token_host":           "oauth2.googleapis.com",
        "token_path_prefix":    "/token",
        "resource_host":        "pubsub.googleapis.com",
        "resource_path_prefix": "/v1",
        "client_id":            "your-dpop-client"
      }'

The proxy generates a per-mapping signing key on creation. It never leaves the proxy.

Workload usage

The workload makes plain HTTPS calls to the resource host. The proxy adds the DPoP proof header and the bound access token.


3. Upstream mTLS

Some upstreams require client-certificate authentication. The private key for that client cert must never reach the workload. Excalibur holds the cert + key, terminates the workload's plain TLS, and re-handshakes upstream with mTLS.

Create a mapping

curl -X POST http://localhost:8443/api/crypto/mtls/mappings \
  -H "Content-Type: application/json" \
  -d '{
        "provider_id":          "partner-api",
        "target_authority":     "internal-api.acme",
        "real_client_cert_pem": "…",
        "real_client_key_pem":  "…"
      }'

The response includes a surrogate_authority value the proxy uses internally for restoration tracking; the workload never sees it.

Workload usage

Just curl https://internal-api.acme/.... The TLS handshake from the workload is the proxy's MITM cert. The TLS handshake from the proxy to internal-api.acme carries the real client certificate.


4. AWS SigV4

Every AWS API call needs a SigV4 signature computed from an IAM access key. With SigV4 mediation the workload makes calls to *.amazonaws.com with no AWS credentials at all — Excalibur holds the access key, signs the canonical request (including streaming body chunks and trailer checksums), and forwards.

Create a mapping

curl -X POST http://localhost:8443/api/crypto/sigv4/mappings \
  -H "Content-Type: application/json" \
  -d '{
        "provider_id":           "aws-prod-s3",
        "target_host":           "s3.eu-west-1.amazonaws.com",
        "path_prefix":           "/",
        "region":                "eu-west-1",
        "service":               "s3",
        "real_access_key_id":     "AKIA…",
        "real_secret_access_key": "…"
      }'

Workload usage

curl https://s3.eu-west-1.amazonaws.com/your-bucket/your-key
# returns 200 with the object body — no AWS_ACCESS_KEY_ID anywhere on the workload

What the proxy signs upstream:

  • Authorization: AWS4-HMAC-SHA256 Credential=AKIA…/…/eu-west-1/s3/aws4_request, SignedHeaders=…, Signature=…
  • All required x-amz-* headers
  • x-amz-content-sha256 (for streaming + trailer-checksum signing)
  • The whole canonical-request hash

The workload never carries any of it.

Streaming + trailer checksums

SigV4 mediation handles the awkward AWS modes:

  • STREAMING-AWS4-HMAC-SHA256-PAYLOAD — chunked uploads
  • STREAMING-UNSIGNED-PAYLOAD-TRAILER — chunk trailer checksums
  • Presigned URLs (signing happens at URL construction time)
  • EventStream framing

For multipart uploads the per-part canonical request is re-signed; the workload's PUT body streams through unchanged.


Choosing between substitution and crypto mediation

If the upstream wants… Use
Authorization: Bearer <static-token> Placeholder substitution
Authorization: Bearer <token derived from a private-key-signed assertion> JWT Bearer mediation
Authorization: DPoP <token> + DPoP: <signed-proof> DPoP mediation
Mutual TLS handshake with a client cert mTLS mediation
Authorization: AWS4-HMAC-SHA256 … SigV4 mediation
OAuth refresh-token flow OAuth2 refresh mediation (built-in adapter)

Crypto-mediated mappings can coexist with placeholder-substituted flows on the same proxy. The choice is per-route, not global.


What gets recorded

Each family writes its own create / sign / exchange events. All are visible on the Audit page filtered by type=*_signed, type=*_mapping_created, or type=*_exchange. The hash chain covers them like every other audit event — see Responding to an incident.


What this gives you

The same property the rest of Excalibur gives you, applied to upstream signing keys:

The signing key never resides on the workload. The workload sends plain HTTP. The signature is produced once per request, by the proxy, from key material the workload has no read path to.

Rotation is the same one-line operator action as for any other credential — see Rotating a credential.