chore: client certificates api review (#31826)

This commit is contained in:
Max Schmitt 2024-07-23 22:56:36 +02:00 committed by GitHub
parent 383e4b3c73
commit c4862c022c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 283 additions and 375 deletions

View file

@ -523,14 +523,17 @@ Does not enforce fixed viewport, allows resizing window in the headed mode.
## context-option-clientCertificates
- `clientCertificates` <[Array]<[Object]>>
- `url` <[string]> Glob pattern to match the URLs that the certificate is valid for.
- `certs` <[Array]<[Object]>> List of client certificates to be used.
- `origin` <[string]> Glob pattern to match against the request origin that the certificate is valid for.
- `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.
- `pfxPath` ?<[string]> Path to the PFX or PKCS12 encoded private key and certificate chain.
- `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 origins, the `origin` property should be provided with a glob pattern to match the origins that the certificate is valid for.
:::note
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';
export default defineConfig({
projects: [
{
name: 'Microsoft Edge',
use: {
...devices['Desktop Edge'],
clientCertificates: [{
url: 'https://example.com/**',
certs: [{
origin: 'https://example.com',
certPath: './cert.pem',
keyPath: './key.pem',
passphrase: 'mysecretpassword',
}],
}],
},
},
]
});
```

View file

@ -550,20 +550,16 @@ function toAcceptDownloadsProtocol(acceptDownloads?: boolean) {
return 'deny';
}
export async function toClientCertificatesProtocol(clientCertificates?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!clientCertificates)
export async function toClientCertificatesProtocol(certs?: BrowserContextOptions['clientCertificates']): Promise<channels.PlaywrightNewRequestParams['clientCertificates']> {
if (!certs)
return undefined;
return await Promise.all(clientCertificates.map(async clientCertificate => {
return {
url: clientCertificate.url,
certs: await Promise.all(clientCertificate.certs.map(async cert => {
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,
};
}))
};
}));
}

View file

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

View file

@ -337,13 +337,11 @@ scheme.PlaywrightNewRequestParams = tObject({
ignoreHTTPSErrors: tOptional(tBoolean),
extraHTTPHeaders: tOptional(tArray(tType('NameValue'))),
clientCertificates: tOptional(tArray(tObject({
url: tString,
certs: tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary),
})),
}))),
httpCredentials: tOptional(tObject({
username: tString,
@ -547,13 +545,11 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
url: tString,
certs: tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary),
})),
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
@ -635,13 +631,11 @@ scheme.BrowserNewContextParams = tObject({
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
url: tString,
certs: tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary),
})),
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
@ -706,13 +700,11 @@ scheme.BrowserNewContextForReuseParams = tObject({
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
url: tString,
certs: tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary),
})),
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),
@ -2526,13 +2518,11 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({
})),
ignoreHTTPSErrors: tOptional(tBoolean),
clientCertificates: tOptional(tArray(tObject({
url: tString,
certs: tArray(tObject({
origin: tString,
cert: tOptional(tBinary),
key: tOptional(tBinary),
passphrase: tOptional(tString),
pfx: tOptional(tBinary),
})),
}))),
javaScriptEnabled: tOptional(tBoolean),
bypassCSP: tOptional(tBoolean),

View file

@ -725,12 +725,9 @@ export function verifyGeolocation(geolocation?: types.Geolocation) {
export function verifyClientCertificates(clientCertificates?: channels.BrowserNewContextParams['clientCertificates']) {
if (!clientCertificates)
return;
for (const { url, certs } of clientCertificates) {
if (!url)
throw new Error(`clientCertificates.url is required`);
if (!certs.length)
throw new Error('No certs specified for url: ' + url);
for (const cert of certs) {
for (const cert of clientCertificates) {
if (!cert.origin)
throw new Error(`clientCertificates.origin is required`);
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)
@ -740,7 +737,6 @@ export function verifyClientCertificates(clientCertificates?: channels.BrowserNe
if (cert.pfx && (cert.cert || cert.key))
throw new Error('pfx is specified together with cert, key or passphrase');
}
}
}
export function normalizeProxySettings(proxy: types.ProxySettings): types.ProxySettings {

View file

@ -193,7 +193,7 @@ export abstract class APIRequestContext extends SdkObject {
maxRedirects: params.maxRedirects === 0 ? -1 : params.maxRedirects === undefined ? 20 : params.maxRedirects,
timeout,
deadline,
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.toString()),
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, requestUrl.origin),
__testHookLookup: (params as any).__testHookLookup,
};
if (process.env.PWTEST_UNSUPPORTED_CUSTOM_CA && isUnderTest())
@ -357,7 +357,7 @@ export abstract class APIRequestContext extends SdkObject {
maxRedirects: options.maxRedirects - 1,
timeout: options.timeout,
deadline: options.deadline,
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, url.toString()),
...clientCertificatesToTLSOptions(this._defaultOptions().clientCertificates, url.origin),
__testHookLookup: options.__testHookLookup,
};
// rejectUnauthorized = undefined is treated as true in node 12.

View file

@ -97,7 +97,7 @@ class SocksProxyConnection {
host: this.host,
port: this.port,
rejectUnauthorized: !this.socksProxy.ignoreHTTPSErrors,
...clientCertificatesToTLSOptions(this.socksProxy.clientCertificates, `https://${this.host}:${this.port}/`),
...clientCertificatesToTLSOptions(this.socksProxy.clientCertificates, `https://${this.host}:${this.port}`),
};
if (!net.isIP(this.host))
tlsOptions.servername = this.host;
@ -183,7 +183,7 @@ export function clientCertificatesToTLSOptions(
const matchingCerts = clientCertificates?.filter(c => {
let regex: RegExp | undefined = (c as any)[kClientCertificatesGlobRegex];
if (!regex) {
regex = globToRegex(c.url);
regex = globToRegex(c.origin);
(c as any)[kClientCertificatesGlobRegex] = regex;
}
regex.lastIndex = 0;
@ -196,8 +196,7 @@ export function clientCertificatesToTLSOptions(
key: [] as { pem: Buffer, passphrase?: string }[],
cert: [] as Buffer[],
};
for (const { certs } of matchingCerts) {
for (const cert of certs) {
for (const cert of matchingCerts) {
if (cert.cert)
tlsOptions.cert.push(cert.cert);
if (cert.key)
@ -205,6 +204,5 @@ export function clientCertificatesToTLSOptions(
if (cert.pfx)
tlsOptions.pfx.push({ buf: cert.pfx, passphrase: cert.passphrase });
}
}
return tlsOptions;
}

View file

@ -13166,10 +13166,14 @@ export interface BrowserType<Unused = {}> {
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
* 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.
* certficiate is encrypted. If the certificate is valid only for specific origins, the `origin` property should be
* provided with a glob pattern to match the origins that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -13178,14 +13182,10 @@ export interface BrowserType<Unused = {}> {
*/
clientCertificates?: Array<{
/**
* Glob pattern to match the URLs that the certificate is valid for.
* Glob pattern to match against the request origin that the certificate is valid for.
*/
url: string;
origin: string;
/**
* List of client certificates to be used.
*/
certs: Array<{
/**
* Path to the file with the certificate in PEM format.
*/
@ -13206,7 +13206,6 @@ export interface BrowserType<Unused = {}> {
*/
passphrase?: string;
}>;
}>;
/**
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
@ -15578,10 +15577,14 @@ export interface APIRequest {
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
* 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.
* certficiate is encrypted. If the certificate is valid only for specific origins, the `origin` property should be
* provided with a glob pattern to match the origins that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -15590,14 +15593,10 @@ export interface APIRequest {
*/
clientCertificates?: Array<{
/**
* Glob pattern to match the URLs that the certificate is valid for.
* Glob pattern to match against the request origin that the certificate is valid for.
*/
url: string;
origin: string;
/**
* List of client certificates to be used.
*/
certs: Array<{
/**
* Path to the file with the certificate in PEM format.
*/
@ -15618,7 +15617,6 @@ export interface APIRequest {
*/
passphrase?: string;
}>;
}>;
/**
* An object containing additional HTTP headers to be sent with every request. Defaults to none.
@ -16772,10 +16770,14 @@ export interface Browser extends EventEmitter {
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
* 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.
* certficiate is encrypted. If the certificate is valid only for specific origins, the `origin` property should be
* provided with a glob pattern to match the origins that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -16784,14 +16786,10 @@ export interface Browser extends EventEmitter {
*/
clientCertificates?: Array<{
/**
* Glob pattern to match the URLs that the certificate is valid for.
* Glob pattern to match against the request origin that the certificate is valid for.
*/
url: string;
origin: string;
/**
* List of client certificates to be used.
*/
certs: Array<{
/**
* Path to the file with the certificate in PEM format.
*/
@ -16812,7 +16810,6 @@ export interface Browser extends EventEmitter {
*/
passphrase?: string;
}>;
}>;
/**
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See
@ -20223,10 +20220,14 @@ export interface BrowserContextOptions {
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
* 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.
* certficiate is encrypted. If the certificate is valid only for specific origins, the `origin` property should be
* provided with a glob pattern to match the origins that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -20235,14 +20236,10 @@ export interface BrowserContextOptions {
*/
clientCertificates?: Array<{
/**
* Glob pattern to match the URLs that the certificate is valid for.
* Glob pattern to match against the request origin that the certificate is valid for.
*/
url: string;
origin: string;
/**
* List of client certificates to be used.
*/
certs: Array<{
/**
* Path to the file with the certificate in PEM format.
*/
@ -20263,7 +20260,6 @@ export interface BrowserContextOptions {
*/
passphrase?: string;
}>;
}>;
/**
* Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`. See

View file

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

View file

@ -5202,10 +5202,14 @@ export interface PlaywrightTestOptions {
*/
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
* 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.
* certficiate is encrypted. If the certificate is valid only for specific origins, the `origin` property should be
* provided with a glob pattern to match the origins that the certificate is valid for.
*
* **NOTE** Using Client Certificates in combination with Proxy Servers is not supported.
*
@ -5219,22 +5223,14 @@ export interface PlaywrightTestOptions {
* import { defineConfig } from '@playwright/test';
*
* export default defineConfig({
* projects: [
* {
* name: 'Microsoft Edge',
* use: {
* ...devices['Desktop Edge'],
* clientCertificates: [{
* url: 'https://example.com/**',
* certs: [{
* origin: 'https://example.com',
* certPath: './cert.pem',
* keyPath: './key.pem',
* passphrase: 'mysecretpassword',
* }],
* }],
* },
* },
* ]
* });
* ```
*

View file

@ -582,14 +582,12 @@ export type PlaywrightNewRequestParams = {
ignoreHTTPSErrors?: boolean,
extraHTTPHeaders?: NameValue[],
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
httpCredentials?: {
username: string,
password: string,
@ -615,14 +613,12 @@ export type PlaywrightNewRequestOptions = {
ignoreHTTPSErrors?: boolean,
extraHTTPHeaders?: NameValue[],
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
httpCredentials?: {
username: string,
password: string,
@ -968,14 +964,12 @@ export type BrowserTypeLaunchPersistentContextParams = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -1050,14 +1044,12 @@ export type BrowserTypeLaunchPersistentContextOptions = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -1167,14 +1159,12 @@ export type BrowserNewContextParams = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -1235,14 +1225,12 @@ export type BrowserNewContextOptions = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -1306,14 +1294,12 @@ export type BrowserNewContextForReuseParams = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -1374,14 +1360,12 @@ export type BrowserNewContextForReuseOptions = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -4582,14 +4566,12 @@ export type AndroidDeviceLaunchBrowserParams = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,
@ -4648,14 +4630,12 @@ export type AndroidDeviceLaunchBrowserOptions = {
},
ignoreHTTPSErrors?: boolean,
clientCertificates?: {
url: string,
certs: {
origin: string,
cert?: Binary,
key?: Binary,
passphrase?: string,
pfx?: Binary,
}[],
}[],
javaScriptEnabled?: boolean,
bypassCSP?: boolean,
userAgent?: string,

View file

@ -445,12 +445,7 @@ ContextOptions:
items:
type: object
properties:
url: string
certs:
type: array
items:
type: object
properties:
origin: string
cert: binary?
key: binary?
passphrase: string?
@ -700,12 +695,7 @@ Playwright:
items:
type: object
properties:
url: string
certs:
type: array
items:
type: object
properties:
origin: string
cert: binary?
key: binary?
passphrase: string?

View file

@ -79,28 +79,23 @@ test.skip(({ mode }) => mode !== 'default');
const kDummyFileName = __filename;
const kValidationSubTests: [BrowserContextOptions, string][] = [
[{ clientCertificates: [{ url: 'test', certs: [] }] }, 'No certs specified for url: test'],
[{ clientCertificates: [{ url: 'test', certs: [{}] }] }, 'None of cert, key, passphrase or pfx is specified'],
[{ clientCertificates: [{ origin: 'test' }] }, 'None of cert, key, passphrase or pfx is specified'],
[{
clientCertificates: [{
url: 'test',
certs: [{
origin: 'test',
certPath: kDummyFileName,
keyPath: kDummyFileName,
pfxPath: kDummyFileName,
passphrase: kDummyFileName,
}]
}]
}, 'pfx is specified together with cert, key or passphrase'],
[{
proxy: { server: 'http://localhost:8080' },
clientCertificates: [{
url: 'test',
certs: [{
origin: 'test',
certPath: kDummyFileName,
keyPath: kDummyFileName,
}]
}]
}, 'Cannot specify both proxy and clientCertificates'],
];
@ -122,12 +117,10 @@ test.describe('fetch', () => {
test('should keep supporting http', async ({ playwright, server, asset }) => {
const request = await playwright.request.newContext({
clientCertificates: [{
url: server.PREFIX,
certs: [{
origin: new URL(server.PREFIX).origin,
certPath: asset('client-certificates/client/trusted/cert.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}],
});
const response = await request.get(server.PREFIX + '/one-style.html');
expect(response.url()).toBe(server.PREFIX + '/one-style.html');
@ -140,12 +133,10 @@ test.describe('fetch', () => {
const serverURL = await startCCServer();
const request = await playwright.request.newContext({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
certPath: asset('client-certificates/client/self-signed/cert.pem'),
keyPath: asset('client-certificates/client/self-signed/key.pem'),
}],
}],
});
const response = await request.get(serverURL);
expect(response.url()).toBe(serverURL);
@ -158,12 +149,10 @@ test.describe('fetch', () => {
const serverURL = await startCCServer();
const request = await playwright.request.newContext({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
certPath: asset('client-certificates/client/trusted/cert.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}],
});
const response = await request.get(serverURL);
expect(response.url()).toBe(serverURL);
@ -176,12 +165,10 @@ test.describe('fetch', () => {
const serverURL = await startCCServer();
const request = await playwright.request.newContext({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
certPath: asset('client-certificates/client/trusted/cert.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}],
});
const page = await browser.newPage({ ignoreHTTPSErrors: true });
await page.route('**/*', async route => {
@ -205,12 +192,10 @@ test.describe('browser', () => {
test('should keep supporting http', async ({ browser, server, asset }) => {
const page = await browser.newPage({
clientCertificates: [{
url: server.PREFIX,
certs: [{
origin: new URL(server.PREFIX).origin,
certPath: asset('client-certificates/client/trusted/cert.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}],
});
await page.goto(server.PREFIX + '/one-style.html');
await expect(page.getByText('hello, world!')).toBeVisible();
@ -222,12 +207,10 @@ test.describe('browser', () => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({
clientCertificates: [{
url: 'https://not-matching.com',
certs: [{
origin: 'https://not-matching.com',
certPath: asset('client-certificates/client/trusted/cert.pem'),
keyPath: asset('client-certificates/client/trusted/key.pem'),
}],
}],
});
await page.goto(serverURL);
await expect(page.getByText('Sorry, but you need to provide a client certificate to continue.')).toBeVisible();
@ -238,12 +221,10 @@ test.describe('browser', () => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
certPath: asset('client-certificates/client/self-signed/cert.pem'),
keyPath: asset('client-certificates/client/self-signed/key.pem'),
}],
}],
});
await page.goto(serverURL);
await expect(page.getByText('Sorry Bob, certificates from Bob are not welcome here')).toBeVisible();
@ -254,12 +235,10 @@ test.describe('browser', () => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const page = await browser.newPage({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
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();
@ -269,12 +248,10 @@ test.describe('browser', () => {
test('should have ignoreHTTPSErrors=false by default', async ({ browser, httpsServer, asset, browserName, platform }) => {
const page = await browser.newPage({
clientCertificates: [{
url: 'https://just-there-that-the-client-certificates-proxy-server-is-getting-launched.com',
certs: [{
origin: 'https://just-there-that-the-client-certificates-proxy-server-is-getting-launched.com',
certPath: asset('client-certificates/client/trusted/cert.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 expect(page.getByText('Playwright client-certificate error')).toBeVisible();
@ -292,12 +269,10 @@ test.describe('browser', () => {
const serverURL = await startCCServer({ useFakeLocalhost: browserName === 'webkit' && process.platform === 'darwin' });
const { page } = await launchPersistent({
clientCertificates: [{
url: serverURL,
certs: [{
origin: new URL(serverURL).origin,
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();