Authenticate Your Bridge

Configure header-based, OAuth2, or mTLS authentication for your bridge

When the ledger calls your bridge, it can include authentication credentials in the outgoing HTTP request. You configure this through the secure property on the bridge record. The secure property is an array of rules, each setting one or more HTTP headers. Rules are processed sequentially in the order they appear.

Header-based authentication

Header authentication adds custom HTTP headers to every request the ledger sends to your bridge. This is the simplest scheme and works well for API keys or custom tokens.

Each rule requires three fields: schema set to "header", key for the header name, and value for the header content. Use secret references ({{ secret.* }}) for sensitive values so the ledger stores them encrypted.

{
  "secure": [
    {
      "schema": "header",
      "key": "X-API-Key",
      "value": "{{ secret.processorApiKey }}"
    },
    {
      "schema": "header",
      "key": "X-Client-ID",
      "value": "{{ secret.clientIdentifier }}"
    }
  ]
}

This configuration adds X-API-Key and X-Client-ID headers to every outgoing request. The actual values are resolved from the bridge's encrypted secret store at request time.

Header keys are case-sensitive and sent exactly as configured. If multiple header rules use the same key, the last rule processed overwrites previous values.

OAuth2

OAuth2 uses the client-credentials flow to obtain an access token and adds an Authorization: Bearer [token] header to outgoing requests automatically. The rule requires schema set to "oauth2", a clientId, a clientSecret (secret reference), and a tokenUrl.

{
  "secure": [
    {
      "schema": "oauth2",
      "clientId": "ledger-client",
      "clientSecret": "{{ secret.bankOAuthSecret }}",
      "tokenUrl": "https://auth.bank.example.com/token"
    }
  ]
}

The ledger calls the token endpoint with a Basic authorization header containing the base64-encoded clientId:clientSecret. The token endpoint must return a JSON body with access_token (string) and optionally expires_in (seconds).

Token caching and expiration follows this priority:

  • If the token is a JWT with an exp claim, that claim determines expiration.
  • If the token lacks an exp claim but the response body includes expires_in, that value is used.
  • If the token is a JWT without exp and no expires_in is provided, it is treated as never expiring.
  • If the token is not a JWT and no expires_in is provided, it is fetched fresh before every request (never cached).

Tokens with a lifetime under 60 seconds are never cached. The ledger fetches a new token before each request to the bridge.

mTLS

Mutual TLS authentication presents a client certificate when connecting to the bridge. Configure it with schema set to "mtls", a public key, and a secret (private key, stored encrypted).

{
  "secure": [
    {
      "schema": "mtls",
      "public": "<MTLS public key>",
      "secret": "<MTLS private key>"
    }
  ]
}

Combining authentication methods

Bridges can have multiple rules in their secure array. Rules are applied sequentially. This lets you combine different authentication mechanisms on a single bridge.

If both a header rule and OAuth2 set the Authorization header, the last rule in the array wins. When combining OAuth2 with header authentication, use header rules for non-Authorization headers.

{
  "handle": "bank-integration",
  "config": {
    "server": "https://bank-api.example.com/v2"
  },
  "secure": [
    {
      "schema": "header",
      "key": "X-Institution-ID",
      "value": "{{ secret.institutionId }}"
    },
    {
      "schema": "header",
      "key": "X-Request-ID",
      "value": "ledger-bridge-request"
    },
    {
      "schema": "oauth2",
      "clientId": "bank-integration-client",
      "clientSecret": "{{ secret.oauthSecret }}",
      "tokenUrl": "https://auth.bank.example.com/oauth/token"
    }
  ]
}

This configuration results in three headers on every request: X-Institution-ID (resolved from secret store), X-Request-ID (static value), and Authorization: Bearer [token] (from OAuth2).

Verifying ledger requests

The authentication methods above cover the outbound direction: how the ledger authenticates itself to your bridge. You also need to verify the inbound direction: that requests arriving at your bridge actually come from the ledger, not a third party spoofing ledger calls.

The ledger signs every request it sends to your bridge using the Ed25519-v2 algorithm. The signature is embedded in the request body as part of the intent proof chain. When your bridge receives a prepare, commit, or abort call, the payload includes the intent record with its accumulated proofs — each one cryptographically signed by the ledger.

The @minka/bridge-sdk verifies the ledger signature automatically before calling your adapter methods. You do not need to implement signature verification manually when using the SDK. If you need to verify the ledger's public key explicitly — for example, to authorize a specific ledger instance — use the ledgerSdk.proofs API:

// In your adapter, verify the request came from the expected ledger signer
await this.ledgerSdk.proofs
  .ledger()
  .expect({
    public: process.env.LEDGER_PUBLIC_KEY,
  })
  .verify(context.intent)

The ledger's public key is available from your Minka Ledger administrator or from the ledger's GET /v2/signers/:handle endpoint after connecting with minka server connect.

In production, always configure LEDGER_PUBLIC_KEY in your .env and verify it against incoming requests. Without this check, any caller that knows your bridge URL could submit fraudulent prepare calls.

mTLS certificate requirements

When using mTLS, the certificate must be in PEM format. The public field holds the certificate or public key, and secret holds the private key (stored encrypted in the bridge record). For development, you can generate a self-signed pair with:

$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes \
    -subj "/CN=bridge-client"

For production, use a certificate issued by your organization's CA or a trusted public CA. The ledger validates that the presented certificate matches the configured public key.

On this page