chore: client certificates api review

This commit is contained in:
Max Schmitt 2024-07-23 20:06:09 +02:00
parent 383e4b3c73
commit 358b10addf
13 changed files with 234 additions and 326 deletions

View file

@ -524,13 +524,16 @@ Does not enforce fixed viewport, allows resizing window in the headed mode.
## context-option-clientCertificates ## context-option-clientCertificates
- `clientCertificates` <[Array]<[Object]>> - `clientCertificates` <[Array]<[Object]>>
- `url` <[string]> Glob pattern to match the URLs that the certificate is valid for. - `url` <[string]> Glob pattern to match the URLs that the certificate is valid for.
- `certs` <[Array]<[Object]>> List of client certificates to be used. - `certPath` ?<[string]> Path to the file with the certificate in PEM format.
- `certPath` ?<[string]> Path to the file with the certificate in PEM format. - `keyPath` ?<[string]> Path to the file with the private key in PEM format.
- `keyPath` ?<[string]> Path to the file with the private key in PEM format. - `pfxPath` ?<[string]> Path to the PFX or PKCS12 encoded private key and certificate chain.
- `pfxPath` ?<[string]> Path to 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).
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 private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided with a glob pattern to match the URLs that the certificate is valid for. 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. If the certificate is valid only for specific URLs, the `url` property should be provided with a glob pattern to match the URLs 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

@ -148,22 +148,14 @@ export default defineConfig({
import { defineConfig } from '@playwright/test'; import { defineConfig } from '@playwright/test';
export default defineConfig({ export default defineConfig({
projects: [ use: {
{ clientCertificates: [{
name: 'Microsoft Edge', url: 'https://example.com',
use: { certPath: './cert.pem',
...devices['Desktop Edge'], keyPath: './key.pem',
clientCertificates: [{ passphrase: 'mysecretpassword',
url: 'https://example.com/**', }],
certs: [{ },
certPath: './cert.pem',
keyPath: './key.pem',
passphrase: 'mysecretpassword',
}],
}],
},
},
]
}); });
``` ```

View file

@ -550,20 +550,16 @@ function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
return 'deny'; return 'deny';
} }
export async function toClientCertificatesProtocol(clientCertificates?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> { export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!clientCertificates) if (!certs)
return undefined; return undefined;
return await Promise.all(clientCertificates.map(async clientCertificate => { return await Promise.all(certs.map(async cert => {
return { return {
url: clientCertificate.url, url: cert.url,
certs: await Promise.all(clientCertificate.certs.map(async cert => { cert: cert.certPath ? await fs.promises.readFile(cert.certPath) : undefined,
return { key: cert.keyPath ? await fs.promises.readFile(cert.keyPath) : undefined,
cert: cert.certPath ? await fs.promises.readFile(cert.certPath) : undefined, pfx: cert.pfxPath ? await fs.promises.readFile(cert.pfxPath) : undefined,
key: cert.keyPath ? await fs.promises.readFile(cert.keyPath) : undefined, passphrase: cert.passphrase,
pfx: cert.pfxPath ? await fs.promises.readFile(cert.pfxPath) : undefined,
passphrase: cert.passphrase,
};
}))
}; };
})); }));
} }

View file

@ -49,12 +49,10 @@ export const kLifecycleEvents: Set<LifecycleEvent> = new Set(['load', 'domconten
export type ClientCertificate = { export type ClientCertificate = {
url: string; url: string;
certs: { certPath?: string;
certPath?: string; keyPath?: string;
keyPath?: string; pfxPath?: string;
pfxPath?: string; passphrase?: string;
passphrase?: string;
}[];
}; };
export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'viewport' | 'noDefaultViewport' | 'extraHTTPHeaders' | 'clientCertificates' | 'storageState' | 'recordHar' | 'colorScheme' | 'reducedMotion' | 'forcedColors' | 'acceptDownloads'> & { export type BrowserContextOptions = Omit<channels.BrowserNewContextOptions, 'viewport' | 'noDefaultViewport' | 'extraHTTPHeaders' | 'clientCertificates' | 'storageState' | 'recordHar' | 'colorScheme' | 'reducedMotion' | 'forcedColors' | 'acceptDownloads'> & {

View file

@ -338,12 +338,10 @@ scheme.PlaywrightNewRequestParams = tObject({
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))), extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
url: tString, url: tString,
certs: tArray(tObject({ cert: tOptional(tBinary),
cert: tOptional(tBinary), key: tOptional(tBinary),
key: tOptional(tBinary), passphrase: tOptional(tString),
passphrase: tOptional(tString), pfx: tOptional(tBinary),
pfx: tOptional(tBinary),
})),
}))), }))),
httpCredentials: tOptional(tObject({ httpCredentials: tOptional(tObject({
username: tString, username: tString,
@ -548,12 +546,10 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({
ignoreHTTPSErrors: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
url: tString, url: tString,
certs: tArray(tObject({ cert: tOptional(tBinary),
cert: tOptional(tBinary), key: tOptional(tBinary),
key: tOptional(tBinary), passphrase: tOptional(tString),
passphrase: tOptional(tString), pfx: tOptional(tBinary),
pfx: tOptional(tBinary),
})),
}))), }))),
javaScriptEnabled: tOptional(tBoolean), javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean), bypassCSP: tOptional(tBoolean),
@ -636,12 +632,10 @@ scheme.BrowserNewContextParams = tObject({
ignoreHTTPSErrors: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
url: tString, url: tString,
certs: tArray(tObject({ cert: tOptional(tBinary),
cert: tOptional(tBinary), key: tOptional(tBinary),
key: tOptional(tBinary), passphrase: tOptional(tString),
passphrase: tOptional(tString), pfx: tOptional(tBinary),
pfx: tOptional(tBinary),
})),
}))), }))),
javaScriptEnabled: tOptional(tBoolean), javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean), bypassCSP: tOptional(tBoolean),
@ -707,12 +701,10 @@ scheme.BrowserNewContextForReuseParams = tObject({
ignoreHTTPSErrors: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
url: tString, url: tString,
certs: tArray(tObject({ cert: tOptional(tBinary),
cert: tOptional(tBinary), key: tOptional(tBinary),
key: tOptional(tBinary), passphrase: tOptional(tString),
passphrase: tOptional(tString), pfx: tOptional(tBinary),
pfx: tOptional(tBinary),
})),
}))), }))),
javaScriptEnabled: tOptional(tBoolean), javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean), bypassCSP: tOptional(tBoolean),
@ -2527,12 +2519,10 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({
ignoreHTTPSErrors: tOptional(tBoolean), ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({ clientCertificates: tOptional(tArray(tObject({
url: tString, url: tString,
certs: tArray(tObject({ cert: tOptional(tBinary),
cert: tOptional(tBinary), key: tOptional(tBinary),
key: tOptional(tBinary), passphrase: tOptional(tString),
passphrase: tOptional(tString), pfx: tOptional(tBinary),
pfx: tOptional(tBinary),
})),
}))), }))),
javaScriptEnabled: tOptional(tBoolean), javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean), bypassCSP: tOptional(tBoolean),

View file

@ -725,21 +725,17 @@ export function verifyGeolocation(geolocation?: types.Geolocation) {
export function verifyClientCertificates(clientCertificates?: channels.BrowserNewContextParams['clientCertificates']) { export function verifyClientCertificates(clientCertificates?: channels.BrowserNewContextParams['clientCertificates']) {
if (!clientCertificates) if (!clientCertificates)
return; return;
for (const { url, certs } of clientCertificates) { for (const cert of clientCertificates) {
if (!url) if (!cert.url)
throw new Error(`clientCertificates.url is required`); throw new Error(`clientCertificates.url is required`);
if (!certs.length) if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx)
throw new Error('No certs specified for url: ' + url); throw new Error('None of cert, key, passphrase or pfx is specified');
for (const cert of certs) { if (cert.cert && !cert.key)
if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx) throw new Error('cert is specified without key');
throw new Error('None of cert, key, passphrase or pfx is specified'); if (!cert.cert && cert.key)
if (cert.cert && !cert.key) throw new Error('key is specified without cert');
throw new Error('cert is specified without key'); if (cert.pfx && (cert.cert || cert.key))
if (!cert.cert && cert.key) throw new Error('pfx is specified together with cert, key or passphrase');
throw new Error('key is specified without cert');
if (cert.pfx && (cert.cert || cert.key))
throw new Error('pfx is specified together with cert, key or passphrase');
}
} }
} }

View file

@ -196,15 +196,13 @@ export function clientCertificatesToTLSOptions(
key: [] as { pem: Buffer, passphrase?: string }[], key: [] as { pem: Buffer, passphrase?: string }[],
cert: [] as Buffer[], cert: [] as Buffer[],
}; };
for (const { certs } of matchingCerts) { for (const cert of matchingCerts) {
for (const cert of certs) { if (cert.cert)
if (cert.cert) tlsOptions.cert.push(cert.cert);
tlsOptions.cert.push(cert.cert); if (cert.key)
if (cert.key) tlsOptions.key.push({ pem: cert.key, passphrase: cert.passphrase });
tlsOptions.key.push({ pem: cert.key, passphrase: cert.passphrase }); if (cert.pfx)
if (cert.pfx) tlsOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase });
tlsOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase });
}
} }
return tlsOptions; return tlsOptions;
} }

View file

@ -13166,9 +13166,13 @@ export interface BrowserType<Unused = {}> {
chromiumSandbox?: boolean; chromiumSandbox?: boolean;
/** /**
* 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 * 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 * single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided * certficiate is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided
* with a glob pattern to match the URLs that the certificate is valid for. * with a glob pattern to match the URLs 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.
@ -13183,29 +13187,24 @@ export interface BrowserType<Unused = {}> {
url: string; url: string;
/** /**
* List of client certificates to be used. * Path to the file with the certificate in PEM format.
*/ */
certs: Array<{ certPath?: string;
/**
* Path to the file with the certificate in PEM format.
*/
certPath?: string;
/** /**
* 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;
/** /**
* 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;
/** /**
* Passphrase for the private key (PEM or PFX). * Passphrase for the private key (PEM or PFX).
*/ */
passphrase?: string; passphrase?: string;
}>;
}>; }>;
/** /**
@ -15578,9 +15577,13 @@ export interface APIRequest {
baseURL?: string; baseURL?: string;
/** /**
* 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 * 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 * single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided * certficiate is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided
* with a glob pattern to match the URLs that the certificate is valid for. * with a glob pattern to match the URLs 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.
@ -15595,29 +15598,24 @@ export interface APIRequest {
url: string; url: string;
/** /**
* List of client certificates to be used. * Path to the file with the certificate in PEM format.
*/ */
certs: Array<{ certPath?: string;
/**
* Path to the file with the certificate in PEM format.
*/
certPath?: string;
/** /**
* 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;
/** /**
* 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;
/** /**
* Passphrase for the private key (PEM or PFX). * Passphrase for the private key (PEM or PFX).
*/ */
passphrase?: string; passphrase?: string;
}>;
}>; }>;
/** /**
@ -16772,9 +16770,13 @@ export interface Browser extends EventEmitter {
bypassCSP?: boolean; bypassCSP?: boolean;
/** /**
* 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 * 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 * single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided * certficiate is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided
* with a glob pattern to match the URLs that the certificate is valid for. * with a glob pattern to match the URLs 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.
@ -16789,29 +16791,24 @@ export interface Browser extends EventEmitter {
url: string; url: string;
/** /**
* List of client certificates to be used. * Path to the file with the certificate in PEM format.
*/ */
certs: Array<{ certPath?: string;
/**
* Path to the file with the certificate in PEM format.
*/
certPath?: string;
/** /**
* 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;
/** /**
* 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;
/** /**
* Passphrase for the private key (PEM or PFX). * Passphrase for the private key (PEM or PFX).
*/ */
passphrase?: string; passphrase?: string;
}>;
}>; }>;
/** /**
@ -20223,9 +20220,13 @@ export interface BrowserContextOptions {
bypassCSP?: boolean; bypassCSP?: boolean;
/** /**
* 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 * 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 * single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided * certficiate is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided
* with a glob pattern to match the URLs that the certificate is valid for. * with a glob pattern to match the URLs 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.
@ -20240,29 +20241,24 @@ export interface BrowserContextOptions {
url: string; url: string;
/** /**
* List of client certificates to be used. * Path to the file with the certificate in PEM format.
*/ */
certs: Array<{ certPath?: string;
/**
* Path to the file with the certificate in PEM format.
*/
certPath?: string;
/** /**
* 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;
/** /**
* 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;
/** /**
* Passphrase for the private key (PEM or PFX). * Passphrase for the private key (PEM or PFX).
*/ */
passphrase?: string; passphrase?: string;
}>;
}>; }>;
/** /**

View file

@ -479,12 +479,10 @@ function resolveFileToConfig(file: string | undefined) {
type ClientCertificates = NonNullable<PlaywrightTestOptions['clientCertificates']>; type ClientCertificates = NonNullable<PlaywrightTestOptions['clientCertificates']>;
function resolveClientCerticates(clientCertificates: ClientCertificates): ClientCertificates { function resolveClientCerticates(clientCertificates: ClientCertificates): ClientCertificates {
for (const { certs } of clientCertificates) { for (const cert of clientCertificates) {
for (const cert of certs) { cert.certPath = resolveFileToConfig(cert.certPath);
cert.certPath = resolveFileToConfig(cert.certPath); cert.keyPath = resolveFileToConfig(cert.keyPath);
cert.keyPath = resolveFileToConfig(cert.keyPath); cert.pfxPath = resolveFileToConfig(cert.pfxPath);
cert.pfxPath = resolveFileToConfig(cert.pfxPath);
}
} }
return clientCertificates; return clientCertificates;
} }

View file

@ -5202,9 +5202,13 @@ export interface PlaywrightTestOptions {
*/ */
colorScheme: ColorScheme; colorScheme: ColorScheme;
/** /**
* 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 * 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 * single `pfxPath` to load the client certificate. Optionally, `passphrase` property should be provided if the
* private key is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided * certficiate is encrypted. If the certificate is valid only for specific URLs, the `url` property should be provided
* with a glob pattern to match the URLs that the certificate is valid for. * with a glob pattern to match the URLs 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.
@ -5219,22 +5223,14 @@ export interface PlaywrightTestOptions {
* import { defineConfig } from '@playwright/test'; * import { defineConfig } from '@playwright/test';
* *
* export default defineConfig({ * export default defineConfig({
* projects: [ * use: {
* { * clientCertificates: [{
* name: 'Microsoft Edge', * url: 'https://example.com',
* use: { * certPath: './cert.pem',
* ...devices['Desktop Edge'], * keyPath: './key.pem',
* clientCertificates: [{ * passphrase: 'mysecretpassword',
* url: 'https://example.com/**', * }],
* certs: [{ * },
* certPath: './cert.pem',
* keyPath: './key.pem',
* passphrase: 'mysecretpassword',
* }],
* }],
* },
* },
* ]
* }); * });
* ``` * ```
* *

View file

@ -583,12 +583,10 @@ export type PlaywrightNewRequestParams = {
extraHTTPHeaders?: NameValue[], extraHTTPHeaders?: NameValue[],
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
httpCredentials?: { httpCredentials?: {
username: string, username: string,
@ -616,12 +614,10 @@ export type PlaywrightNewRequestOptions = {
extraHTTPHeaders?: NameValue[], extraHTTPHeaders?: NameValue[],
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
httpCredentials?: { httpCredentials?: {
username: string, username: string,
@ -969,12 +965,10 @@ export type BrowserTypeLaunchPersistentContextParams = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -1051,12 +1045,10 @@ export type BrowserTypeLaunchPersistentContextOptions = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -1168,12 +1160,10 @@ export type BrowserNewContextParams = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -1236,12 +1226,10 @@ export type BrowserNewContextOptions = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -1307,12 +1295,10 @@ export type BrowserNewContextForReuseParams = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -1375,12 +1361,10 @@ export type BrowserNewContextForReuseOptions = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -4583,12 +4567,10 @@ export type AndroidDeviceLaunchBrowserParams = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,
@ -4649,12 +4631,10 @@ export type AndroidDeviceLaunchBrowserOptions = {
ignoreHTTPSErrors?: boolean, ignoreHTTPSErrors?: boolean,
clientCertificates?: { clientCertificates?: {
url: string, url: string,
certs: { cert?: Binary,
cert?: Binary, key?: Binary,
key?: Binary, passphrase?: string,
passphrase?: string, pfx?: Binary,
pfx?: Binary,
}[],
}[], }[],
javaScriptEnabled?: boolean, javaScriptEnabled?: boolean,
bypassCSP?: boolean, bypassCSP?: boolean,

View file

@ -446,15 +446,10 @@ ContextOptions:
type: object type: object
properties: properties:
url: string url: string
certs: cert: binary?
type: array key: binary?
items: passphrase: string?
type: object pfx: binary?
properties:
cert: binary?
key: binary?
passphrase: string?
pfx: binary?
javaScriptEnabled: boolean? javaScriptEnabled: boolean?
bypassCSP: boolean? bypassCSP: boolean?
userAgent: string? userAgent: string?
@ -701,15 +696,10 @@ Playwright:
type: object type: object
properties: properties:
url: string url: string
certs: cert: binary?
type: array key: binary?
items: passphrase: string?
type: object pfx: binary?
properties:
cert: binary?
key: binary?
passphrase: string?
pfx: binary?
httpCredentials: httpCredentials:
type: object? type: object?
properties: properties:

View file

@ -79,27 +79,22 @@ test.skip(({ mode }) => mode !== 'default');
const kDummyFileName = __filename; const kDummyFileName = __filename;
const kValidationSubTests: [BrowserContextOptions, string][] = [ const kValidationSubTests: [BrowserContextOptions, string][] = [
[{ clientCertificates: [{ url: 'test', certs: [] }] }, 'No certs specified for url: test'], [{ clientCertificates: [{ url: 'test' }] }, 'None of cert, key, passphrase or pfx is specified'],
[{ clientCertificates: [{ url: 'test', certs: [{}] }] }, 'None of cert, key, passphrase or pfx is specified'],
[{ [{
clientCertificates: [{ clientCertificates: [{
url: 'test', url: 'test',
certs: [{ certPath: kDummyFileName,
certPath: kDummyFileName, keyPath: kDummyFileName,
keyPath: kDummyFileName, pfxPath: kDummyFileName,
pfxPath: kDummyFileName, passphrase: kDummyFileName,
passphrase: kDummyFileName,
}]
}] }]
}, 'pfx is specified together with cert, key or passphrase'], }, 'pfx is specified together with cert, key or passphrase'],
[{ [{
proxy: { server: 'http://localhost:8080' }, proxy: { server: 'http://localhost:8080' },
clientCertificates: [{ clientCertificates: [{
url: 'test', url: 'test',
certs: [{ certPath: kDummyFileName,
certPath: kDummyFileName, keyPath: kDummyFileName,
keyPath: kDummyFileName,
}]
}] }]
}, 'Cannot specify both proxy and clientCertificates'], }, 'Cannot specify both proxy and clientCertificates'],
]; ];
@ -123,10 +118,8 @@ test.describe('fetch', () => {
const request = await playwright.request.newContext({ const request = await playwright.request.newContext({
clientCertificates: [{ clientCertificates: [{
url: server.PREFIX, url: server.PREFIX,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
const response = await request.get(server.PREFIX + '/one-style.html'); const response = await request.get(server.PREFIX + '/one-style.html');
@ -141,10 +134,8 @@ test.describe('fetch', () => {
const request = await playwright.request.newContext({ const request = await playwright.request.newContext({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/self-signed/cert.pem'),
certPath: asset('client-certificates/client/self-signed/cert.pem'), keyPath: asset('client-certificates/client/self-signed/key.pem'),
keyPath: asset('client-certificates/client/self-signed/key.pem'),
}],
}], }],
}); });
const response = await request.get(serverURL); const response = await request.get(serverURL);
@ -159,10 +150,8 @@ test.describe('fetch', () => {
const request = await playwright.request.newContext({ const request = await playwright.request.newContext({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
const response = await request.get(serverURL); const response = await request.get(serverURL);
@ -177,10 +166,8 @@ test.describe('fetch', () => {
const request = await playwright.request.newContext({ const request = await playwright.request.newContext({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
const page = await browser.newPage({ ignoreHTTPSErrors: true }); const page = await browser.newPage({ ignoreHTTPSErrors: true });
@ -206,10 +193,8 @@ test.describe('browser', () => {
const page = await browser.newPage({ const page = await browser.newPage({
clientCertificates: [{ clientCertificates: [{
url: server.PREFIX, url: server.PREFIX,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
await page.goto(server.PREFIX + '/one-style.html'); await page.goto(server.PREFIX + '/one-style.html');
@ -223,10 +208,8 @@ test.describe('browser', () => {
const page = await browser.newPage({ const page = await browser.newPage({
clientCertificates: [{ clientCertificates: [{
url: 'https://not-matching.com', url: 'https://not-matching.com',
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
await page.goto(serverURL); await page.goto(serverURL);
@ -239,10 +222,8 @@ test.describe('browser', () => {
const page = await browser.newPage({ const page = await browser.newPage({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/self-signed/cert.pem'),
certPath: asset('client-certificates/client/self-signed/cert.pem'), keyPath: asset('client-certificates/client/self-signed/key.pem'),
keyPath: asset('client-certificates/client/self-signed/key.pem'),
}],
}], }],
}); });
await page.goto(serverURL); await page.goto(serverURL);
@ -255,10 +236,8 @@ test.describe('browser', () => {
const page = await browser.newPage({ const page = await browser.newPage({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
await page.goto(serverURL); await page.goto(serverURL);
@ -270,10 +249,8 @@ test.describe('browser', () => {
const page = await browser.newPage({ const page = await browser.newPage({
clientCertificates: [{ clientCertificates: [{
url: 'https://just-there-that-the-client-certificates-proxy-server-is-getting-launched.com', url: 'https://just-there-that-the-client-certificates-proxy-server-is-getting-launched.com',
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
await page.goto(browserName === 'webkit' && platform === 'darwin' ? httpsServer.EMPTY_PAGE.replace('localhost', 'local.playwright') : httpsServer.EMPTY_PAGE); await page.goto(browserName === 'webkit' && platform === 'darwin' ? httpsServer.EMPTY_PAGE.replace('localhost', 'local.playwright') : httpsServer.EMPTY_PAGE);
@ -293,10 +270,8 @@ test.describe('browser', () => {
const { page } = await launchPersistent({ const { page } = await launchPersistent({
clientCertificates: [{ clientCertificates: [{
url: serverURL, url: serverURL,
certs: [{ certPath: asset('client-certificates/client/trusted/cert.pem'),
certPath: asset('client-certificates/client/trusted/cert.pem'), keyPath: asset('client-certificates/client/trusted/key.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}], }],
}); });
await page.goto(serverURL); await page.goto(serverURL);