Axios with SSL Certificate

Mutual TLS (SSL client certificate)

As of TAS 5.17, you can configure a client SSL certificate for HTTPS requests (mutual TLS) using the applySslConfig() method. The method returns the same client instance, so it can be chained with further configuration (e.g. setVaultHeader).

The way the certificate value is obtained differs between versions 5.17 and 5.19 — see the sections below.

Parameters

The method accepts a single configuration object opts:

Parameter

Type

Description

ca

string | Buffer | VaultSecretRef

CA bundle (certificate authority). Optional.

cert

string | Buffer | VaultSecretRef

Client certificate. Optional.

key

string | Buffer | VaultSecretRef

Private key for the client certificate. Optional.

passphrase

string

Password for the private key (if the key is encrypted). Optional.

rejectUnauthorized

boolean

Whether to verify the server's SSL certificate. Default value true.

ciphers

string

List of allowed TLS ciphers (OpenSSL format). Optional. Used for compatibility with older services — see the warning in the practical example.

TAS 5.17 — certificate value via lib.getCertificate()

In version 5.17, the Vault does not yet consolidate certificates. The certificate value must be loaded the "old way" using lib.getCertificate(certName, encoding?) (default encoding binary) and passed into applySslConfig() as a string or Buffer.

const client = axios.getAxios({ baseURL: 'https://api.mutual-tls.example.com' });
client.applySslConfig({
ca: lib.getCertificate('mtls-ca'),
cert: lib.getCertificate('mtls-client-cert'),
key: lib.getCertificate('mtls-client-key'),
});

const response = client.get('/secure-endpoint');
return response.data;
The absolute path to the certificate file is returned by lib.getCertificatePath(certName).

TAS 5.18 — certificates and external resources from the Vault via vault.get()

As of version 5.18, the Vault consolidates both sensitive values (API keys, passwords, tokens) and certificates and external resources (Administration → Vault, External resources tab). In a calculation, everything is loaded uniformly via vault.get(name), which returns a VaultSecretRef — this cannot be serialized or logged and is passed directly into applySslConfig().

const client = axios.getAxios({ baseURL: 'https://api.mutual-tls.example.com' });
client.applySslConfig({
ca: vault.get('MTLS_CA'),
cert: vault.get('MTLS_CLIENT_CERT'),
key: vault.get('MTLS_CLIENT_KEY'),
});

const response = client.get('/secure-endpoint');
return response.data;
The same approach (vault.get(...)) is used in 5.18 to load values from external resources defined at the server configuration level, e.g. in the .env file.

Setting rejectUnauthorized: false disables verification of the server certificate and exposes the connection to the risk of a MITM attack. Use only for testing against self-signed certificates, never in production.

Practical example — SOAP call with a client certificate

The most common real-world use of mutual TLS is calling an older SOAP endpoint (typically a government service or a banking interface) that also authenticates with a client certificate. Compared to the simple client.get() above, three things are added: an XML body via Buffer.from, the SOAPAction header, and the use of requestRaw. Let's walk through the example step by step.

1. Request body via Buffer.from

By default, axios serializes the body as JSON. A SOAP envelope, however, is XML — to have axios send it unchanged, wrap it in a binary Buffer:

const payload = Buffer.from(xml, 'utf-8');

The xml variable contains a ready-made SOAP envelope as text; Buffer.from(..., 'utf-8') converts it into binary data.

2. Client with an explicit timeout

SOAP services tend to be slower, so set a sufficient timeout (in milliseconds). The default value of 0 follows the global calculation timeout (120 s), which may not be enough for a slow service:

const client = axios.getAxios({
    timeout: 300000
});

3. Setting the certificate

The configuration is the same as in the examples above — here with values from the Vault via lib.getCertificate() (TAS 5.17). In addition, two security-reducing parameters appear, which legacy SOAP services often require:

client.applySslConfig({
    cert: lib.getCertificate('client.pem'),
    key: lib.getCertificate('client.key'),
    rejectUnauthorized: false,
    ciphers: "DEFAULT@SECLEVEL=0"
});

The ciphers parameter defines the allowed TLS ciphers. The value DEFAULT@SECLEVEL=0 lowers the OpenSSL security level and allows older, otherwise disabled ciphers and weaker keys — without it, a connection to older services may fail to be established.

The combination of rejectUnauthorized: false and ciphers: "DEFAULT@SECLEVEL=0" deliberately reduces connection security — it disables server certificate verification (MITM risk) and allows outdated ciphers. Use it only where the counterparty genuinely requires it. If the service runs on a modern certificate, omit both options.

4. Sending via requestRaw

SOAP is sent using the POST method. The SOAPAction header is mandatory in SOAP 1.1 — the value '""' (empty quotes) is used when the service does not require a specific action. requestRaw is used because the status code and headers are usually needed from the SOAP response:

const result = client.requestRaw({
    method: 'POST',
    url,
    data: payload,
    headers: {
        'SOAPAction': '""'
    }
});
requestRaw (as of version 5.7.37) returns the full response { data, status, statusText, headers } and does not throw an exception on an error status — so check the status code manually (e.g. if (result.status !== 200)). The classic methods (get, post, …) return data directly and throw an exception on error; use those for regular JSON APIs where you don't need the status or headers.

Complete code
const payload = Buffer.from(xml, 'utf-8');

const client = axios.getAxios({
    timeout: 300000
});
client.applySslConfig({
    cert: lib.getCertificate('client.pem'),
    key: lib.getCertificate('client.key'),
    rejectUnauthorized: false,
    ciphers: "DEFAULT@SECLEVEL=0"
});

const result = client.requestRaw({
    method: 'POST',
    url,
    data: payload,
    headers: {
        'SOAPAction': '""'
    }
});
The call is synchronousresult holds the response directly. Never use async, await, .then(), or Promise; the TAS transpilation would cause the calculation to fail silently.

Frantisek Brych Updated by Frantisek Brych

Axios with Vault

Contact

Syca (opens in a new tab)

Powered by HelpDocs (opens in a new tab)