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.
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 uploadsSTREAMING-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.
