feat(client-certificates): allow passing certificates from memory (#32210)

This commit is contained in:
Max Schmitt 2024-08-19 09:24:32 +02:00 committed by GitHub
parent 74f5ce5489
commit 010778f6c5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 132 additions and 30 deletions

View File

@ -531,15 +531,18 @@ Does not enforce fixed viewport, allows resizing window in the headed mode.
- `clientCertificates` <[Array]<[Object]>>
- `origin` <[string]> Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port.
- `certPath` ?<[path]> Path to the file with the certificate in PEM format.
- `cert` ?<[Buffer]> Direct value of the certificate in PEM format.
- `keyPath` ?<[path]> Path to the file with the private key in PEM format.
- `key` ?<[Buffer]> Direct value of the private key in PEM format.
- `pfxPath` ?<[path]> Path to the PFX or PKCS12 encoded private key and certificate chain.
- `pfx` ?<[Buffer]> Direct value of the PFX or PKCS12 encoded private key and certificate chain.
- `passphrase` ?<[string]> Passphrase for the private key (PEM or PFX).
TLS Client Authentication allows the server to request a client certificate and verify it.
**Details**
An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that the certificate is valid for.
An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`, a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally, `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided with an exact match to the request origin that the certificate is valid for.
:::note
Using Client Certificates in combination with Proxy Servers is not supported.

View File

@ -552,13 +552,19 @@ function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!certs)
return undefined;
return await Promise.all(certs.map(async cert => {
return {
origin: cert.origin,
cert: cert.certPath ? await fs.promises.readFile(cert.certPath) : undefined,
key: cert.keyPath ? await fs.promises.readFile(cert.keyPath) : undefined,
pfx: cert.pfxPath ? await fs.promises.readFile(cert.pfxPath) : undefined,
passphrase: cert.passphrase,
const bufferizeContent = async (value?: Buffer, path?: string): Promise<Buffer | undefined> => {
if (value)
return value;
if (path)
return await fs.promises.readFile(path);
};
}));
return await Promise.all(certs.map(async cert => ({
origin: cert.origin,
cert: await bufferizeContent(cert.cert, cert.certPath),
key: await bufferizeContent(cert.key, cert.keyPath),
pfx: await bufferizeContent(cert.pfx, cert.pfxPath),
passphrase: cert.passphrase,
})));
}

View File

@ -49,8 +49,11 @@ export const kLifecycleEvents: Set<LifecycleEvent> = new Set(['load', 'domconten
export type ClientCertificate = {
origin: string;
cert?: Buffer;
certPath?: string;
key?: Buffer;
keyPath?: string;
pfx?: Buffer;
pfxPath?: string;
passphrase?: string;
};

View File

@ -9138,10 +9138,10 @@ export interface Browser {
*
* **Details**
*
* An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a
* single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that
* the certificate is valid for.
* An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,
* a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,
* `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided
* with an exact match to the request origin that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -9159,16 +9159,31 @@ export interface Browser {
*/
certPath?: string;
/**
* Direct value of the certificate in PEM format.
*/
cert?: Buffer;
/**
* Path to the file with the private key in PEM format.
*/
keyPath?: string;
/**
* Direct value of the private key in PEM format.
*/
key?: Buffer;
/**
* Path to the PFX or PKCS12 encoded private key and certificate chain.
*/
pfxPath?: string;
/**
* Direct value of the PFX or PKCS12 encoded private key and certificate chain.
*/
pfx?: Buffer;
/**
* Passphrase for the private key (PEM or PFX).
*/
@ -13850,10 +13865,10 @@ export interface BrowserType<Unused = {}> {
*
* **Details**
*
* An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a
* single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that
* the certificate is valid for.
* An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,
* a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,
* `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided
* with an exact match to the request origin that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -13871,16 +13886,31 @@ export interface BrowserType<Unused = {}> {
*/
certPath?: string;
/**
* Direct value of the certificate in PEM format.
*/
cert?: Buffer;
/**
* Path to the file with the private key in PEM format.
*/
keyPath?: string;
/**
* Direct value of the private key in PEM format.
*/
key?: Buffer;
/**
* Path to the PFX or PKCS12 encoded private key and certificate chain.
*/
pfxPath?: string;
/**
* Direct value of the PFX or PKCS12 encoded private key and certificate chain.
*/
pfx?: Buffer;
/**
* Passphrase for the private key (PEM or PFX).
*/
@ -16259,10 +16289,10 @@ export interface APIRequest {
*
* **Details**
*
* An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a
* single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that
* the certificate is valid for.
* An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,
* a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,
* `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided
* with an exact match to the request origin that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -16280,16 +16310,31 @@ export interface APIRequest {
*/
certPath?: string;
/**
* Direct value of the certificate in PEM format.
*/
cert?: Buffer;
/**
* Path to the file with the private key in PEM format.
*/
keyPath?: string;
/**
* Direct value of the private key in PEM format.
*/
key?: Buffer;
/**
* Path to the PFX or PKCS12 encoded private key and certificate chain.
*/
pfxPath?: string;
/**
* Direct value of the PFX or PKCS12 encoded private key and certificate chain.
*/
pfx?: Buffer;
/**
* Passphrase for the private key (PEM or PFX).
*/
@ -20600,10 +20645,10 @@ export interface BrowserContextOptions {
*
* **Details**
*
* An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a
* single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that
* the certificate is valid for.
* An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,
* a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,
* `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided
* with an exact match to the request origin that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -20621,16 +20666,31 @@ export interface BrowserContextOptions {
*/
certPath?: string;
/**
* Direct value of the certificate in PEM format.
*/
cert?: Buffer;
/**
* Path to the file with the private key in PEM format.
*/
keyPath?: string;
/**
* Direct value of the private key in PEM format.
*/
key?: Buffer;
/**
* Path to the PFX or PKCS12 encoded private key and certificate chain.
*/
pfxPath?: string;
/**
* Direct value of the PFX or PKCS12 encoded private key and certificate chain.
*/
pfx?: Buffer;
/**
* Passphrase for the private key (PEM or PFX).
*/

View File

@ -5206,10 +5206,10 @@ export interface PlaywrightTestOptions {
*
* **Details**
*
* An array of client certificates to be used. Each certificate object must have both `certPath` and `keyPath` or a
* single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* certficiate is encrypted. The `origin` property should be provided with an exact match to the request origin that
* the certificate is valid for.
* An array of client certificates to be used. Each certificate object must have either both `certPath` and `keyPath`,
* a single `pfxPath`, or their corresponding direct value equivalents (`cert` and `key`, or `pfx`). Optionally,
* `passphrase` property should be provided if the certificate is encrypted. The `origin` property should be provided
* with an exact match to the request origin that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*

View File

@ -303,6 +303,21 @@ test.describe('browser', () => {
await page.close();
});
test('should pass with matching certificates when passing as content', async ({ browser, startCCServer, asset, browserName }) => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({
ignoreHTTPSErrors: true,
clientCertificates: [{
origin: new URL(serverURL).origin,
cert: await fs.promises.readFile(asset('client-certificates/client/trusted/cert.pem')),
key: await fs.promises.readFile(asset('client-certificates/client/trusted/key.pem')),
}],
});
await page.goto(serverURL);
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
await page.close();
});
test('should not hang on tls errors during TLS 1.2 handshake', async ({ browser, asset, platform, browserName }) => {
for (const tlsVersion of ['TLSv1.3', 'TLSv1.2'] as const) {
await test.step(`TLS version: ${tlsVersion}`, async () => {
@ -360,6 +375,21 @@ test.describe('browser', () => {
await page.close();
});
test('should pass with matching certificates in pfx format when passing as content', async ({ browser, startCCServer, asset, browserName }) => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({
ignoreHTTPSErrors: true,
clientCertificates: [{
origin: new URL(serverURL).origin,
pfx: await fs.promises.readFile(asset('client-certificates/client/trusted/cert.pfx')),
passphrase: 'secure'
}],
});
await page.goto(serverURL);
await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!');
await page.close();
});
test('should fail with matching certificates in legacy pfx format', async ({ browser, startCCServer, asset, browserName }) => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
await expect(browser.newPage({