feat: Restrain sending http credentials on a specific origin (#20374)
For security purpose, we would like to restrain sending HTTP credentials to only the specified server. The idea is to give the ability to specify a origin (scheme://host:port) additionally to current pair username/password. When an authorization response is received from servers, the credentials are sent only if the server origin in the request matches case insensitive the specified origin.
This commit is contained in:
parent
c3d7ffb773
commit
591e4ea976
|
|
@ -560,8 +560,10 @@ Whether to emulate network being offline. Defaults to `false`.
|
|||
- `httpCredentials` <[Object]>
|
||||
- `username` <[string]>
|
||||
- `password` <[string]>
|
||||
- `origin` ?<[string]> Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
|
||||
Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
If no origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
|
||||
## context-option-colorscheme
|
||||
* langs: js, java
|
||||
|
|
|
|||
|
|
@ -328,6 +328,7 @@ scheme.PlaywrightNewRequestParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
proxy: tOptional(tObject({
|
||||
server: tString,
|
||||
|
|
@ -543,6 +544,7 @@ scheme.BrowserTypeLaunchPersistentContextParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
deviceScaleFactor: tOptional(tNumber),
|
||||
isMobile: tOptional(tBoolean),
|
||||
|
|
@ -614,6 +616,7 @@ scheme.BrowserNewContextParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
deviceScaleFactor: tOptional(tNumber),
|
||||
isMobile: tOptional(tBoolean),
|
||||
|
|
@ -674,6 +677,7 @@ scheme.BrowserNewContextForReuseParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
deviceScaleFactor: tOptional(tNumber),
|
||||
isMobile: tOptional(tBoolean),
|
||||
|
|
@ -845,6 +849,7 @@ scheme.BrowserContextSetHTTPCredentialsParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
});
|
||||
scheme.BrowserContextSetHTTPCredentialsResult = tOptional(tObject({}));
|
||||
|
|
@ -2201,6 +2206,7 @@ scheme.ElectronLaunchParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
ignoreHTTPSErrors: tOptional(tBoolean),
|
||||
locale: tOptional(tString),
|
||||
|
|
@ -2409,6 +2415,7 @@ scheme.AndroidDeviceLaunchBrowserParams = tObject({
|
|||
httpCredentials: tOptional(tObject({
|
||||
username: tString,
|
||||
password: tString,
|
||||
origin: tOptional(tString),
|
||||
})),
|
||||
deviceScaleFactor: tOptional(tNumber),
|
||||
isMobile: tOptional(tBoolean),
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export class CRNetworkManager {
|
|||
private _parentManager: CRNetworkManager | null;
|
||||
private _requestIdToRequest = new Map<string, InterceptableRequest>();
|
||||
private _requestIdToRequestWillBeSentEvent = new Map<string, Protocol.Network.requestWillBeSentPayload>();
|
||||
private _credentials: {username: string, password: string} | null = null;
|
||||
private _credentials: {origin?: string, username: string, password: string} | null = null;
|
||||
private _attemptedAuthentications = new Set<string>();
|
||||
private _userRequestInterceptionEnabled = false;
|
||||
private _protocolRequestInterceptionEnabled = false;
|
||||
|
|
@ -161,19 +161,26 @@ export class CRNetworkManager {
|
|||
|
||||
_onAuthRequired(event: Protocol.Fetch.authRequiredPayload) {
|
||||
let response: 'Default' | 'CancelAuth' | 'ProvideCredentials' = 'Default';
|
||||
const shouldProvideCredentials = this._shouldProvideCredentials(event.request.url);
|
||||
if (this._attemptedAuthentications.has(event.requestId)) {
|
||||
response = 'CancelAuth';
|
||||
} else if (this._credentials) {
|
||||
} else if (shouldProvideCredentials) {
|
||||
response = 'ProvideCredentials';
|
||||
this._attemptedAuthentications.add(event.requestId);
|
||||
}
|
||||
const { username, password } = this._credentials || { username: undefined, password: undefined };
|
||||
const { username, password } = shouldProvideCredentials && this._credentials ? this._credentials : { username: undefined, password: undefined };
|
||||
this._client._sendMayFail('Fetch.continueWithAuth', {
|
||||
requestId: event.requestId,
|
||||
authChallengeResponse: { response, username, password },
|
||||
});
|
||||
}
|
||||
|
||||
_shouldProvideCredentials(url: string): boolean {
|
||||
if (!this._credentials)
|
||||
return false;
|
||||
return !this._credentials.origin || new URL(url).origin.toLowerCase() === this._credentials.origin.toLowerCase();
|
||||
}
|
||||
|
||||
_onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
|
||||
if (!event.networkId) {
|
||||
// Fetch without networkId means that request was not recongnized by inspector, and
|
||||
|
|
|
|||
|
|
@ -339,7 +339,7 @@ export abstract class APIRequestContext extends SdkObject {
|
|||
}
|
||||
if (response.statusCode === 401 && !getHeader(options.headers, 'authorization')) {
|
||||
const auth = response.headers['www-authenticate'];
|
||||
const credentials = this._defaultOptions().httpCredentials;
|
||||
const credentials = this._getHttpCredentials(url);
|
||||
if (auth?.trim().startsWith('Basic') && credentials) {
|
||||
const { username, password } = credentials;
|
||||
const encoded = Buffer.from(`${username || ''}:${password || ''}`).toString('base64');
|
||||
|
|
@ -426,6 +426,12 @@ export abstract class APIRequestContext extends SdkObject {
|
|||
request.end();
|
||||
});
|
||||
}
|
||||
|
||||
private _getHttpCredentials(url: URL) {
|
||||
if (!this._defaultOptions().httpCredentials?.origin || url.origin.toLowerCase() === this._defaultOptions().httpCredentials?.origin?.toLowerCase())
|
||||
return this._defaultOptions().httpCredentials;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class SafeEmptyStreamTransform extends Transform {
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ export type PageScreencastOptions = {
|
|||
export type Credentials = {
|
||||
username: string;
|
||||
password: string;
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
export type Geolocation = {
|
||||
|
|
|
|||
|
|
@ -732,8 +732,8 @@ export class WKPage implements PageDelegate {
|
|||
}
|
||||
|
||||
async updateHttpCredentials() {
|
||||
const credentials = this._browserContext._options.httpCredentials || { username: '', password: '' };
|
||||
await this._pageProxySession.send('Emulation.setAuthCredentials', { username: credentials.username, password: credentials.password });
|
||||
const credentials = this._browserContext._options.httpCredentials || { username: '', password: '', origin: '' };
|
||||
await this._pageProxySession.send('Emulation.setAuthCredentials', { username: credentials.username, password: credentials.password, origin: credentials.origin });
|
||||
}
|
||||
|
||||
async updateFileChooserInterception() {
|
||||
|
|
|
|||
48
packages/playwright-core/types/types.d.ts
vendored
48
packages/playwright-core/types/types.d.ts
vendored
|
|
@ -12340,12 +12340,18 @@ export interface BrowserType<Unused = {}> {
|
|||
headless?: boolean;
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -13733,12 +13739,18 @@ export interface AndroidDevice {
|
|||
hasTouch?: boolean;
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -14443,12 +14455,18 @@ export interface APIRequest {
|
|||
extraHTTPHeaders?: { [key: string]: string; };
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -15590,12 +15608,18 @@ export interface Browser extends EventEmitter {
|
|||
hasTouch?: boolean;
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -16451,12 +16475,18 @@ export interface Electron {
|
|||
};
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: {
|
||||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -18673,7 +18703,8 @@ export interface BrowserContextOptions {
|
|||
hasTouch?: boolean;
|
||||
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials?: HTTPCredentials;
|
||||
|
||||
|
|
@ -18968,6 +18999,11 @@ export interface HTTPCredentials {
|
|||
username: string;
|
||||
|
||||
password: string;
|
||||
|
||||
/**
|
||||
* Restrain sending http credentials on specific origin (scheme://host:port).
|
||||
*/
|
||||
origin?: string;
|
||||
}
|
||||
|
||||
export interface Geolocation {
|
||||
|
|
|
|||
3
packages/playwright-test/types/test.d.ts
vendored
3
packages/playwright-test/types/test.d.ts
vendored
|
|
@ -3491,7 +3491,8 @@ export interface PlaywrightTestOptions {
|
|||
*/
|
||||
hasTouch: boolean;
|
||||
/**
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication).
|
||||
* Credentials for [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication). If no
|
||||
* origin is specified, the username and password are sent to any servers upon unauthorized responses.
|
||||
*/
|
||||
httpCredentials: HTTPCredentials | undefined;
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -573,6 +573,7 @@ export type PlaywrightNewRequestParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
proxy?: {
|
||||
server: string,
|
||||
|
|
@ -595,6 +596,7 @@ export type PlaywrightNewRequestOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
proxy?: {
|
||||
server: string,
|
||||
|
|
@ -953,6 +955,7 @@ export type BrowserTypeLaunchPersistentContextParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1023,6 +1026,7 @@ export type BrowserTypeLaunchPersistentContextOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1118,6 +1122,7 @@ export type BrowserNewContextParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1175,6 +1180,7 @@ export type BrowserNewContextOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1235,6 +1241,7 @@ export type BrowserNewContextForReuseParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1292,6 +1299,7 @@ export type BrowserNewContextForReuseOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -1556,12 +1564,14 @@ export type BrowserContextSetHTTPCredentialsParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
};
|
||||
export type BrowserContextSetHTTPCredentialsOptions = {
|
||||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
};
|
||||
export type BrowserContextSetHTTPCredentialsResult = void;
|
||||
|
|
@ -3965,6 +3975,7 @@ export type ElectronLaunchParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
locale?: string,
|
||||
|
|
@ -3998,6 +4009,7 @@ export type ElectronLaunchOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
ignoreHTTPSErrors?: boolean,
|
||||
locale?: string,
|
||||
|
|
@ -4367,6 +4379,7 @@ export type AndroidDeviceLaunchBrowserParams = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
@ -4422,6 +4435,7 @@ export type AndroidDeviceLaunchBrowserOptions = {
|
|||
httpCredentials?: {
|
||||
username: string,
|
||||
password: string,
|
||||
origin?: string,
|
||||
},
|
||||
deviceScaleFactor?: number,
|
||||
isMobile?: boolean,
|
||||
|
|
|
|||
|
|
@ -448,6 +448,7 @@ ContextOptions:
|
|||
properties:
|
||||
username: string
|
||||
password: string
|
||||
origin: string?
|
||||
deviceScaleFactor: number?
|
||||
isMobile: boolean?
|
||||
hasTouch: boolean?
|
||||
|
|
@ -654,6 +655,7 @@ Playwright:
|
|||
properties:
|
||||
username: string
|
||||
password: string
|
||||
origin: string?
|
||||
proxy:
|
||||
type: object?
|
||||
properties:
|
||||
|
|
@ -1065,6 +1067,7 @@ BrowserContext:
|
|||
properties:
|
||||
username: string
|
||||
password: string
|
||||
origin: string?
|
||||
|
||||
setNetworkInterceptionPatterns:
|
||||
parameters:
|
||||
|
|
@ -3088,6 +3091,7 @@ Electron:
|
|||
properties:
|
||||
username: string
|
||||
password: string
|
||||
origin: string?
|
||||
ignoreHTTPSErrors: boolean?
|
||||
locale: string?
|
||||
offline: boolean?
|
||||
|
|
|
|||
|
|
@ -76,3 +76,61 @@ it('should return resource body', async ({ browser, server }) => {
|
|||
expect((await response.body()).toString()).toContain('Playground');
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with correct credentials and matching origin', async ({ browser, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should work with correct credentials and matching origin case insensitive', async ({ browser, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX.toUpperCase() }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fail with correct credentials and mismatching scheme', async ({ browser, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX.replace('http://', 'https://') }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fail with correct credentials and mismatching hostname', async ({ browser, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const hostname = new URL(server.PREFIX).hostname;
|
||||
const origin = server.PREFIX.replace(hostname, 'mismatching-hostname');
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass', origin: origin }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
|
||||
it('should fail with correct credentials and mismatching port', async ({ browser, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const origin = server.PREFIX.replace(server.PORT.toString(), (server.PORT + 1).toString());
|
||||
const context = await browser.newContext({
|
||||
httpCredentials: { username: 'user', password: 'pass', origin: origin }
|
||||
});
|
||||
const page = await context.newPage();
|
||||
const response = await page.goto(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
await context.close();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -97,6 +97,44 @@ it('should return error with wrong credentials', async ({ playwright, server })
|
|||
expect(response.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should work with correct credentials and matching origin', async ({ playwright, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const request = await playwright.request.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX } });
|
||||
const response = await request.get(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should work with correct credentials and matching origin case insensitive', async ({ playwright, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const request = await playwright.request.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX.toUpperCase() } });
|
||||
const response = await request.get(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(200);
|
||||
});
|
||||
|
||||
it('should return error with correct credentials and mismatching scheme', async ({ playwright, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const request = await playwright.request.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: server.PREFIX.replace('http://', 'https://') } });
|
||||
const response = await request.get(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should return error with correct credentials and mismatching hostname', async ({ playwright, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const hostname = new URL(server.PREFIX).hostname;
|
||||
const origin = server.PREFIX.replace(hostname, 'mismatching-hostname');
|
||||
const request = await playwright.request.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: origin } });
|
||||
const response = await request.get(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should return error with correct credentials and mismatching port', async ({ playwright, server }) => {
|
||||
server.setAuth('/empty.html', 'user', 'pass');
|
||||
const origin = server.PREFIX.replace(server.PORT.toString(), (server.PORT + 1).toString());
|
||||
const request = await playwright.request.newContext({ httpCredentials: { username: 'user', password: 'pass', origin: origin } });
|
||||
const response = await request.get(server.EMPTY_PAGE);
|
||||
expect(response.status()).toBe(401);
|
||||
});
|
||||
|
||||
it('should support WWW-Authenticate: Basic', async ({ playwright, server }) => {
|
||||
let credentials;
|
||||
server.setRoute('/empty.html', (req, res) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue