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]>> - `clientCertificates` <[Array]<[Object]>>
- `origin` <[string]> Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port. - `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. - `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. - `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. - `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). - `passphrase` ?<[string]> Passphrase for the private key (PEM or PFX).
TLS Client Authentication allows the server to request a client certificate and verify it. TLS Client Authentication allows the server to request a client certificate and verify it.
**Details** **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 :::note
Using Client Certificates in combination with Proxy Servers is not supported. 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']> { export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!certs) if (!certs)
return undefined; return undefined;
return await Promise.all(certs.map(async cert => {
return { const bufferizeContent = async (value?: Buffer, path?: string): Promise<Buffer | undefined> => {
origin: cert.origin, if (value)
cert: cert.certPath ? await fs.promises.readFile(cert.certPath) : undefined, return value;
key: cert.keyPath ? await fs.promises.readFile(cert.keyPath) : undefined, if (path)
pfx: cert.pfxPath ? await fs.promises.readFile(cert.pfxPath) : undefined, return await fs.promises.readFile(path);
passphrase: cert.passphrase, };
};
})); 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 = { export type ClientCertificate = {
origin: string; origin: string;
cert?: Buffer;
certPath?: string; certPath?: string;
key?: Buffer;
keyPath?: string; keyPath?: string;
pfx?: Buffer;
pfxPath?: string; pfxPath?: string;
passphrase?: string; passphrase?: string;
}; };

View file

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

View file

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

View file

@ -303,6 +303,21 @@ test.describe('browser', () => {
await page.close(); 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 }) => { 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) { for (const tlsVersion of ['TLSv1.3', 'TLSv1.2'] as const) {
await test.step(`TLS version: ${tlsVersion}`, async () => { await test.step(`TLS version: ${tlsVersion}`, async () => {
@ -360,6 +375,21 @@ test.describe('browser', () => {
await page.close(); 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 }) => { 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' }); const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
await expect(browser.newPage({ await expect(browser.newPage({