Skip to content

Unable to pass client certificate from pact-js verifier (mTLS) #1509

@hylmarj

Description

@hylmarj

Software versions

  • OS: _Windows 11 23H2 (22631.5472)
  • *Pact foundation: v15.0.1
  • Pact Foundation CLI: v16.0.7
  • Node Version: ` v22.11.0

Issue Checklist

  • I have upgraded to the latest
  • I have the read the FAQs in the Readme
  • I have triple checked, that there are no unhandled promises in my code and have read the section on intermittent test failures
  • I have set my log level to debug and attached a log file showing the complete request/response cycle
  • For bonus points and virtual high fives, I have created a reproduceable git repository (see below) to illustrate the problem

Expected behaviour

Provider verification started via pact-js Verifier should be able to establish mutual-TLS to the providerBaseUrl when an https.Agent { pfx, passphrase } is supplied (either through requestFilter or https.globalAgent), in the same way it already works when running pact-provider-verifier --tlsclientcertfile ….

Actual behaviour

The verifier spins up its local proxy (127.0.0.1) and the requestFilter receives only that internal request. When the proxy creates the outbound https.request towards the real providerBaseUrl, the configured https.Agent is not* reused, the client certificate is not sent and the TLS handshake fails with alert handshake failure / unknown ca.

  • The same .p12 works in a plain Node smoke-test (https.get).
  • For HTTP providers (no TLS) the verifier works fine.

Steps to reproduce

import fs from 'fs';
import https from 'https';
import { Verifier } from '@pact-foundation/pact';

const mtlsAgent = new https.Agent({
  pfx: fs.readFileSync('./certs/client.p12'),
  passphrase: 'anonymisedPwd',
  rejectUnauthorized: false,   
});

await new Verifier({
  provider: 'MyProvider2',
  providerBaseUrl: 'https://<API_HOST>:8443',
  validateSSL: false,

  // hook only sees proxy request
  requestFilter: (req, _res, next) => {
    req.agent = mtlsAgent;  // client cert
    req.servername = '<API_HOST>';  // SNI
    console.log('hook dump: ', {
      host: req.host, port: req.port, agent: !!req.agent
    });
    next();
  },

  pactBrokerUrl: process.env.PACT_BROKER_BASE_URL,
  pactBrokerToken: process.env.PACT_BROKER_TOKEN,
  publishVerificationResult: true,
  providerVersion: 'demo-1.0.0',
  consumerVersionSelectors: [{ branch: 'main', latest: true }],
  enablePending: true,
  logLevel: 'debug',
}).verifyProvider();

Relevant log files

pact:proxy non-local providerBaseUrl detected, changeOrigin=true
hook dump → { host: '127.0.0.1', port: undefined, agent: true }
write EPROTO 10560000:error:0A000410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure:c:\ws\deps\openssl\openssl\ssl\record\rec_layer_s3.c:1605:SSL alert number 40

We noticed providerTransports[].tls exists in Rust wrappers. Is there an ETA for exposing this in the JavaScript API?
https://github.com/pact-foundation/pact-reference/blob/master/rust/pact_verifier_cli/src/main.rs

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementIndicates new feature requestsgood first issueIndicates a good issue for first-time contributorshelp wantedIndicates that a maintainer wants help on an issue or pull request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions