From 1060f45d72107e0a6c6dd12159f1f9f2cc49f602 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Fri, 2 Oct 2026 08:54:25 -0700 Subject: [PATCH] =?UTF-8?q?Revert=20"fix(client-certificates):=20reestabli?= =?UTF-8?q?sh=20a=20way=20to=20explicitly=20send=20no=20cli=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 59672632a2252f863999bfe98c7e53910f951a99. --- docs/src/api/params.md | 3 +- packages/playwright-client/types/types.d.ts | 44 +++--------- .../src/client/browserContext.ts | 1 - .../playwright-core/src/client/channels.d.ts | 11 --- packages/playwright-core/src/client/types.ts | 1 - .../src/server/browserContext.ts | 5 -- .../playwright-core/src/server/channels.d.ts | 11 --- .../socksClientCertificatesInterceptor.ts | 18 ++--- packages/playwright-core/types/types.d.ts | 44 +++--------- packages/playwright/types/test.d.ts | 6 +- packages/protocol/spec/mixins.yml | 1 - packages/protocol/spec/playwright.yml | 1 - packages/protocol/src/validator.ts | 6 -- tests/library/client-certificates.spec.ts | 72 ------------------- 14 files changed, 25 insertions(+), 199 deletions(-) diff --git a/docs/src/api/params.md b/docs/src/api/params.md index 2a8cee3fec022..1a0be85da5f45 100644 --- a/docs/src/api/params.md +++ b/docs/src/api/params.md @@ -685,7 +685,6 @@ Does not enforce fixed viewport, allows resizing window in the headed mode. - `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). - - `noCertificate` ?<[boolean]> Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. TLS Client Authentication allows the server to request a client certificate and verify it. @@ -693,7 +692,7 @@ TLS Client Authentication allows the server to request a client certificate and 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. -Client certificate authentication is only active when at least one client certificate is provided. If you want to reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. +Client certificate authentication is only active when at least one client certificate is provided. If you want to reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that does not match any of the domains you plan to visit. :::note When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it work by replacing `localhost` with `local.playwright`. diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 1724559292f59..af277e3dc767b 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -11343,10 +11343,8 @@ export interface Browser { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -11392,11 +11390,6 @@ export interface Browser { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -17688,10 +17681,8 @@ export interface BrowserType { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -17737,11 +17728,6 @@ export interface BrowserType { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -19486,10 +19472,8 @@ export interface APIRequest { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -19535,11 +19519,6 @@ export interface APIRequest { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -26692,10 +26671,8 @@ export interface BrowserContextOptions { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -26741,11 +26718,6 @@ export interface BrowserContextOptions { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** diff --git a/packages/playwright-core/src/client/browserContext.ts b/packages/playwright-core/src/client/browserContext.ts index ddebc7b6c613a..fbe49ff548858 100644 --- a/packages/playwright-core/src/client/browserContext.ts +++ b/packages/playwright-core/src/client/browserContext.ts @@ -616,6 +616,5 @@ export async function toClientCertificatesProtocol(certs?: BrowserContextOptions key: await bufferizeContent(cert.key, cert.keyPath), pfx: await bufferizeContent(cert.pfx, cert.pfxPath), passphrase: cert.passphrase, - noCertificate: cert.noCertificate, }))); } diff --git a/packages/playwright-core/src/client/channels.d.ts b/packages/playwright-core/src/client/channels.d.ts index b16202f1dd988..d48d4a5a26077 100644 --- a/packages/playwright-core/src/client/channels.d.ts +++ b/packages/playwright-core/src/client/channels.d.ts @@ -390,7 +390,6 @@ export type AndroidDeviceLaunchBrowserParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -464,7 +463,6 @@ export type AndroidDeviceLaunchBrowserOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -895,7 +893,6 @@ export type BrowserNewContextParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -972,7 +969,6 @@ export type BrowserNewContextOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1052,7 +1048,6 @@ export type BrowserNewContextForReuseParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1129,7 +1124,6 @@ export type BrowserNewContextForReuseOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1248,7 +1242,6 @@ export type BrowserContextInitializer = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1908,7 +1901,6 @@ export type BrowserTypeLaunchPersistentContextParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1998,7 +1990,6 @@ export type BrowserTypeLaunchPersistentContextOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -4804,7 +4795,6 @@ export type PlaywrightNewRequestParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], maxRedirects?: number, httpCredentials?: HttpCredentials[], @@ -4832,7 +4822,6 @@ export type PlaywrightNewRequestOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], maxRedirects?: number, httpCredentials?: HttpCredentials[], diff --git a/packages/playwright-core/src/client/types.ts b/packages/playwright-core/src/client/types.ts index a38339de994c4..89a610732afa5 100644 --- a/packages/playwright-core/src/client/types.ts +++ b/packages/playwright-core/src/client/types.ts @@ -62,7 +62,6 @@ export type ClientCertificate = { pfx?: Buffer; pfxPath?: string; passphrase?: string; - noCertificate?: boolean; }; export type BrowserContextOptions = Omit & { diff --git a/packages/playwright-core/src/server/browserContext.ts b/packages/playwright-core/src/server/browserContext.ts index 38134b7c5e690..1434e9807169f 100644 --- a/packages/playwright-core/src/server/browserContext.ts +++ b/packages/playwright-core/src/server/browserContext.ts @@ -798,11 +798,6 @@ export function verifyClientCertificates(clientCertificates?: types.BrowserConte for (const cert of clientCertificates) { if (!cert.origin) throw new Error(`clientCertificates.origin is required`); - if (cert.noCertificate) { - if (cert.cert || cert.key || cert.passphrase || cert.pfx) - throw new Error('noCertificate is set together with cert, key, passphrase or pfx'); - continue; - } if (!cert.cert && !cert.key && !cert.passphrase && !cert.pfx) throw new Error('None of cert, key, passphrase or pfx is specified'); if (cert.cert && !cert.key) diff --git a/packages/playwright-core/src/server/channels.d.ts b/packages/playwright-core/src/server/channels.d.ts index dd7b4f890204b..dd7258b24bb21 100644 --- a/packages/playwright-core/src/server/channels.d.ts +++ b/packages/playwright-core/src/server/channels.d.ts @@ -391,7 +391,6 @@ export type AndroidDeviceLaunchBrowserParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -465,7 +464,6 @@ export type AndroidDeviceLaunchBrowserOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -896,7 +894,6 @@ export type BrowserNewContextParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -973,7 +970,6 @@ export type BrowserNewContextOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1053,7 +1049,6 @@ export type BrowserNewContextForReuseParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1130,7 +1125,6 @@ export type BrowserNewContextForReuseOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1249,7 +1243,6 @@ export type BrowserContextInitializer = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1909,7 +1902,6 @@ export type BrowserTypeLaunchPersistentContextParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -1999,7 +1991,6 @@ export type BrowserTypeLaunchPersistentContextOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], javaScriptEnabled?: boolean, bypassCSP?: boolean, @@ -4805,7 +4796,6 @@ export type PlaywrightNewRequestParams = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], maxRedirects?: number, httpCredentials?: HttpCredentials[], @@ -4833,7 +4823,6 @@ export type PlaywrightNewRequestOptions = { key?: Binary, passphrase?: string, pfx?: Binary, - noCertificate?: boolean, }[], maxRedirects?: number, httpCredentials?: HttpCredentials[], diff --git a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts index 350272eb6c8d7..2f4afc91bc410 100644 --- a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +++ b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts @@ -161,8 +161,9 @@ class SocksProxyConnection { this._firstPackageReceived = true; // 0x16 is the TLS "handshake" content type. Only intercept it when the origin has a client // certificate; otherwise pass the connection through so the browser talks TLS to the server directly. - if (data[0] === 0x16 && this.socksProxy.secureContextMap.has(normalizeOrigin(`https://${this.host}:${this.port}`))) - this._establishTlsTunnel(this._browserEncrypted, data, this.socksProxy.secureContextMap.get(normalizeOrigin(`https://${this.host}:${this.port}`))); + const secureContext = data[0] === 0x16 ? this.socksProxy.secureContextMap.get(normalizeOrigin(`https://${this.host}:${this.port}`)) : undefined; + if (secureContext) + this._establishTlsTunnel(this._browserEncrypted, data, secureContext); else this._establishPlaintextTunnel(this._browserEncrypted); } @@ -176,7 +177,7 @@ class SocksProxyConnection { this._serverEncrypted.pipe(browserEncrypted); } - private _establishTlsTunnel(browserEncrypted: stream.Duplex, clientHello: Buffer, secureContext?: tls.SecureContext) { + private _establishTlsTunnel(browserEncrypted: stream.Duplex, clientHello: Buffer, secureContext: tls.SecureContext) { const browserALPNProtocols = parseALPNFromClientHello(clientHello) || ['http/1.1']; debugLogger.log('client-certificates', `Browser->Proxy ${this.host}:${this.port} offers ALPN ${browserALPNProtocols}`); @@ -279,7 +280,7 @@ export class ClientCertificatesProxy { _socksProxy: SocksProxy; private _connections: Map = new Map(); ignoreHTTPSErrors: boolean | undefined; - secureContextMap: Map = new Map(); + secureContextMap: Map = new Map(); private _proxy: types.ProxySettings | undefined; private constructor( @@ -322,7 +323,7 @@ export class ClientCertificatesProxy { _initSecureContexts(clientCertificates: types.BrowserContextOptions['clientCertificates']) { // Step 1. Group certificates by origin. - const origin2certs = new Map>(); + const origin2certs = new Map(); for (const cert of clientCertificates || []) { const origin = normalizeOrigin(cert.origin); const certs = origin2certs.get(origin) || []; @@ -332,13 +333,6 @@ export class ClientCertificatesProxy { // Step 2. Create secure contexts for each origin. for (const [origin, certs] of origin2certs) { - const noCertificateCount = certs.filter(cert => cert.noCertificate).length; - if (noCertificateCount > 0 && noCertificateCount < certs.length) - throw new Error(`clientCertificates for origin "${origin}" mix noCertificate with a real certificate`); - if (noCertificateCount > 0) { - this.secureContextMap.set(origin, undefined); - continue; - } try { this.secureContextMap.set(origin, tls.createSecureContext(convertClientCertificatesToTLSOptions(certs))); } catch (error) { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 1724559292f59..af277e3dc767b 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -11343,10 +11343,8 @@ export interface Browser { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -11392,11 +11390,6 @@ export interface Browser { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -17688,10 +17681,8 @@ export interface BrowserType { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -17737,11 +17728,6 @@ export interface BrowserType { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -19486,10 +19472,8 @@ export interface APIRequest { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -19535,11 +19519,6 @@ export interface APIRequest { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** @@ -26692,10 +26671,8 @@ export interface BrowserContextOptions { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. @@ -26741,11 +26718,6 @@ export interface BrowserContextOptions { * Passphrase for the private key (PEM or PFX). */ passphrase?: string; - - /** - * Explicitly send no client certificate for this origin. Must be the only field set besides `origin`. - */ - noCertificate?: boolean; }>; /** diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index 033029cc5a12f..ca74f02412e59 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -7439,10 +7439,8 @@ export interface PlaywrightTestOptions { * with an exact match to the request origin that the certificate is valid for. * * Client certificate authentication is only active when at least one client certificate is provided. If you want to - * reject all client certificates sent by the server for an origin you visit, set `noCertificate` to `true` for that - * origin instead of omitting it: omitting the origin entirely leaves the connection unintercepted, so the server's - * own certificate request still reaches the browser and may trigger a native certificate-selection prompt on some - * platforms. `noCertificate` forces interception for that origin while still presenting no client certificate. + * reject all client certificates sent by the server, you need to provide a client certificate with an `origin` that + * does not match any of the domains you plan to visit. * * **NOTE** When using WebKit on macOS, accessing `localhost` will not pick up client certificates. You can make it * work by replacing `localhost` with `local.playwright`. diff --git a/packages/protocol/spec/mixins.yml b/packages/protocol/spec/mixins.yml index f6e3141bb2be6..d65e620361954 100644 --- a/packages/protocol/spec/mixins.yml +++ b/packages/protocol/spec/mixins.yml @@ -137,7 +137,6 @@ ContextOptions: key: binary? passphrase: string? pfx: binary? - noCertificate: boolean? javaScriptEnabled: boolean? bypassCSP: boolean? userAgent: string? diff --git a/packages/protocol/spec/playwright.yml b/packages/protocol/spec/playwright.yml index 4cb7be2babd89..b4c9651b2e4be 100644 --- a/packages/protocol/spec/playwright.yml +++ b/packages/protocol/spec/playwright.yml @@ -62,7 +62,6 @@ Playwright: key: binary? passphrase: string? pfx: binary? - noCertificate: boolean? maxRedirects: int? httpCredentials: type: array? diff --git a/packages/protocol/src/validator.ts b/packages/protocol/src/validator.ts index 0e0c3caff00de..7c592fa38f272 100644 --- a/packages/protocol/src/validator.ts +++ b/packages/protocol/src/validator.ts @@ -158,7 +158,6 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), javaScriptEnabled: tOptional(tBoolean), bypassCSP: tOptional(tBoolean), @@ -469,7 +468,6 @@ scheme.BrowserNewContextParams = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), javaScriptEnabled: tOptional(tBoolean), bypassCSP: tOptional(tBoolean), @@ -549,7 +547,6 @@ scheme.BrowserNewContextForReuseParams = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), javaScriptEnabled: tOptional(tBoolean), bypassCSP: tOptional(tBoolean), @@ -651,7 +648,6 @@ scheme.BrowserContextInitializer = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), javaScriptEnabled: tOptional(tBoolean), bypassCSP: tOptional(tBoolean), @@ -1059,7 +1055,6 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), javaScriptEnabled: tOptional(tBoolean), bypassCSP: tOptional(tBoolean), @@ -2783,7 +2778,6 @@ scheme.PlaywrightNewRequestParams = tObject({ key: tOptional(tBinary), passphrase: tOptional(tString), pfx: tOptional(tBinary), - noCertificate: tOptional(tBoolean), }))), maxRedirects: tOptional(tInt), httpCredentials: tOptional(tArray(tType('HttpCredentials'))), diff --git a/tests/library/client-certificates.spec.ts b/tests/library/client-certificates.spec.ts index e71403c2a15df..c967ced534ee6 100644 --- a/tests/library/client-certificates.spec.ts +++ b/tests/library/client-certificates.spec.ts @@ -88,13 +88,6 @@ const kValidationSubTests: [BrowserContextOptions, string][] = [ passphrase: kDummyFileName, }] }, 'pfx is specified together with cert, key or passphrase'], - [{ - clientCertificates: [{ - origin: 'test', - noCertificate: true, - certPath: kDummyFileName, - }] - }, 'noCertificate is set together with cert, key, passphrase or pfx'], ]; test.describe('fetch', () => { @@ -112,21 +105,6 @@ test.describe('fetch', () => { await request.dispose(); }); - test('should send no client certificate when noCertificate is set', async ({ playwright, startCCServer }) => { - const serverURL = await startCCServer(); - const request = await playwright.request.newContext({ - ignoreHTTPSErrors: true, - clientCertificates: [{ - origin: new URL(serverURL).origin, - noCertificate: true, - }], - }); - const response = await request.get(serverURL); - expect(response.status()).toBe(401); - expect(await response.text()).toContain('Sorry, but you need to provide a client certificate to continue.'); - await request.dispose(); - }); - test('should keep supporting http', async ({ playwright, server, asset }) => { const request = await playwright.request.newContext({ ignoreHTTPSErrors: true, @@ -383,41 +361,6 @@ test.describe('browser', () => { await page.close(); }); - test('should intercept TLS and send no client certificate when noCertificate is set for the visited origin', { - annotation: { type: 'issue', description: 'https://github.com/microsoft/playwright/issues/42546' }, - }, async ({ browser, startCCServer }) => { - const serverURL = await startCCServer(); - const page = await browser.newPage({ - ignoreHTTPSErrors: true, - clientCertificates: [{ - origin: new URL(serverURL).origin, - noCertificate: true, - }], - }); - const response = await page.goto(serverURL); - // noCertificate forces interception for this origin, so the browser sees the - // proxy's own dummy certificate (CN=localhost) instead of the real - // server's certificate. - const securityDetails = await response.securityDetails(); - expect(securityDetails.subjectName).toBe('localhost'); - // The app layer still receives no client certificate. - await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.'); - await page.close(); - }); - - test('should throw when noCertificate and a real certificate are both set for the same origin', async ({ browser, asset }) => { - await expect(browser.newContext({ - clientCertificates: [ - { origin: 'https://not-matching.com', noCertificate: true }, - { - origin: 'https://not-matching.com', - certPath: asset('client-certificates/client/trusted/cert.pem'), - keyPath: asset('client-certificates/client/trusted/key.pem'), - }, - ], - })).rejects.toThrow('clientCertificates for origin "https://not-matching.com" mix noCertificate with a real certificate'); - }); - test('should fail with no client certificates', async ({ browser, startCCServer, asset, browserName, isMac }) => { const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && isMac }); const page = await browser.newPage({ @@ -1005,20 +948,5 @@ test.describe('browser', () => { await page.goto(serverURL); await expect(page.getByTestId('message')).toHaveText('Hello Alice, your certificate was issued by localhost!'); }); - - test('should intercept TLS and send no client certificate when noCertificate is set for the visited origin', async ({ launchPersistent, startCCServer }) => { - const serverURL = await startCCServer(); - const { page } = await launchPersistent({ - ignoreHTTPSErrors: true, - clientCertificates: [{ - origin: new URL(serverURL).origin, - noCertificate: true, - }], - }); - const response = await page.goto(serverURL); - const securityDetails = await response.securityDetails(); - expect(securityDetails.subjectName).toBe('localhost'); - await expect(page.getByTestId('message')).toHaveText('Sorry, but you need to provide a client certificate to continue.'); - }); }); });