diff --git a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts index 07d0bbbb79..ce8086c821 100644 --- a/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts +++ b/packages/playwright-core/src/server/socksClientCertificatesInterceptor.ts @@ -228,7 +228,13 @@ export function clientCertificatesToTLSOptions( clientCertificates: channels.BrowserNewContextOptions['clientCertificates'], origin: string ): Pick | undefined { - const matchingCerts = clientCertificates?.filter(c => c.origin === origin); + const matchingCerts = clientCertificates?.filter(c => { + try { + return new URL(c.origin).origin === origin; + } catch (error) { + return c.origin === origin; + } + }); if (!matchingCerts || !matchingCerts.length) return; const tlsOptions = { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 1b1cba0b5a..47e263cdb6 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -13182,7 +13182,7 @@ export interface BrowserType { */ clientCertificates?: Array<{ /** - * Exact origin that the certificate is valid for. + * Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port. */ origin: string; @@ -15593,7 +15593,7 @@ export interface APIRequest { */ clientCertificates?: Array<{ /** - * Exact origin that the certificate is valid for. + * Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port. */ origin: string; @@ -16786,7 +16786,7 @@ export interface Browser extends EventEmitter { */ clientCertificates?: Array<{ /** - * Exact origin that the certificate is valid for. + * Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port. */ origin: string; @@ -20236,7 +20236,7 @@ export interface BrowserContextOptions { */ clientCertificates?: Array<{ /** - * Exact origin that the certificate is valid for. + * Exact origin that the certificate is valid for. Origin includes `https` protocol, a hostname and optionally a port. */ origin: string; diff --git a/tests/library/client-certificates.spec.ts b/tests/library/client-certificates.spec.ts index d1b15e4658..c5fc523925 100644 --- a/tests/library/client-certificates.spec.ts +++ b/tests/library/client-certificates.spec.ts @@ -248,6 +248,20 @@ test.describe('browser', () => { await page.close(); }); + test('should pass with matching certificates and trailing slash', async ({ browser, startCCServer, asset, browserName }) => { + const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' }); + const page = await browser.newPage({ + clientCertificates: [{ + origin: serverURL, + certPath: asset('client-certificates/client/trusted/cert.pem'), + keyPath: asset('client-certificates/client/trusted/key.pem'), + }], + }); + await page.goto(serverURL); + await expect(page.getByText('Hello Alice, your certificate was issued by localhost!')).toBeVisible(); + await page.close(); + }); + test('should have ignoreHTTPSErrors=false by default', async ({ browser, httpsServer, asset, browserName, platform }) => { const page = await browser.newPage({ clientCertificates: [{