playwright
playwright copied to clipboard
[Feature] Directly set Proxy-Authorization in Chrome without configuring any other proxy settings
Google Cloud Platform has a feature called Identity Aware Proxy (IAP) that facilitates authentication to back-end cloud services. It's basically a reverse proxy that enforces authentication and applies security policies so the back-end services can just assume that all requests they receive are authorized.
If you need to access an IAP-secured service from inside Google Cloud this authentication happens automatically, but if you need to access an IAP service from outside, it is necessary to supply authentication credentials via headers. This can be done via the Authorization
header, but in some cases (if your back-end app also uses Authorization
) it is necessary to pass these credentials via the Proxy-Authorization
header as a bearer token (not basic auth).
The problem (in Chrome/Chromium anyway) is that Proxy-Authorization
may not be set explicitly via extraHTTPHeaders
. If you do this and then try to navigate to any page Chrome will give you the following error: net::ERR_INVALID_ARGUMENT
. If you configure a proxy this header will be automatically generated, but that won't work in this case because we don't actually want to configure any proxy (there is no applicable proxy address), and we want to use bearer rather than basic auth. I realize that this is a Chrome-specific issue and not directly under PlayWright's control, but I thought I would open an issue anyway to provide a real world example when setting this header is required and to discuss possible solutions or workarounds.
Related issue: https://github.com/microsoft/playwright-python/issues/443
Let's collect upvotes.
The only workaround I was able to find so far is to not test this in Chrome. This is a growing trend among some of our customers - lock everything behind IAP and it's really annoying that a security feature from Chrome's side of things doesn't give us any easy way to override it.
Hi, i'm having the same problem with Proxy-Authorization when using Chrome browser. I can't authorize my Google Cloud IAP, because it has invalid parameters as mentioned in the thread.
Can we fix this? Thanks!
Keen to have this fixed as well
@pavelfeldman, how man upvotes do we need to get this fix prioritized?
more than one year with this issue, I'm also with the exactly same issue
I wonder if loading a Chrome extension with declarativeNetRequest
permission can provide the Proxy-Authorization
header or this will also hit the same wall as the other methods?
manigest.json
{
"manifest_version": 3,
"name": "Playwright Proxy Authenticator",
"version": "1.0",
"declarative_net_request": {
"rule_resources": [
{
"id": "google_iap",
"enabled": true,
"path": "google_iap.json"
}
]
},
"permissions": [
"declarativeNetRequest",
"declarativeNetRequestWithHostAccess",
"declarativeNetRequestFeedback"
]
}
google_iap.json
[
{
"id": 1,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "Proxy-Authorization",
"operation": "set",
"value": "Bearer __OMITTED__ [[Generated by Google IAP API Client]]"
}
]
},
"condition": {
"urlFilter": "*.*",
"resourceTypes": [
"main_frame"
]
}
}
]
I ended solving this problem by installing a chrome extension as part of the test, using persistent Context. I used ModHeader specifically and was able to integrate with Google's IAP (where I needed to set the the proxy-authentication header, since the underlying authentication header was needed for the api the app was using).
Can confirm that Chrome Extension works.
You can build a very simple extension that sets the Proxy-Authorization
header. Note that you need to have a persistent context.
Sharing my code that creates the extension. You can fill in the blanks for how to get the header programmatically
Code:
/**
* @fileoverview
* Create the Google IAP Proxy Authorizer browser extension.
*/
import fs from 'node:fs';
import path from 'node:path';
async function create() {
const root = getRepoRoot();
const destinationDirectory = '/path/to/dist'; // load this via --load-extension in launch args
const header = await getGoogleIAPHeader(); // TODO
const manifest = {
manifest_version: 3,
name: 'Google IAP Proxy Authorizer',
version: '1.0',
// for debugging
// background: {
// service_worker: 'background.js',
// },
declarative_net_request: {
rule_resources: [
{
id: 'ruleset_1',
enabled: true,
path: 'ruleset_1.json',
},
],
},
permissions: [
'declarativeNetRequest',
'declarativeNetRequestWithHostAccess',
'declarativeNetRequestFeedback',
'storage',
'tabs',
],
host_permissions: ['<all_urls>'],
};
const ruleset = [
{
id: 1,
priority: 1,
action: {
type: 'modifyHeaders',
requestHeaders: [
{
header: 'Proxy-Authorization',
operation: 'set',
value: header,
},
],
},
condition: {
requestMethods: ['get', 'post', 'put', 'delete', 'head', 'patch', 'options', 'connect'],
resourceTypes: [
'main_frame',
'sub_frame',
'stylesheet',
'script',
'image',
'font',
'object',
'xmlhttprequest',
'ping',
'csp_report',
'media',
'websocket',
'webtransport',
'webbundle',
'other',
],
},
},
];
fs.mkdirSync(destinationDirectory, { recursive: true });
fs.writeFileSync(
path.join(destinationDirectory, 'manifest.json'),
JSON.stringify(manifest, null, 2),
);
fs.writeFileSync(
path.join(destinationDirectory, 'ruleset_1.json'),
JSON.stringify(ruleset, null, 2),
);
}
create();
I found another work-around for setting Proxy-Authorization
. You can use the Playwright Route API. For example, given the Playwright Context
, you could do something like this:
await context.route(
(_url: URL) => true,
async (route: playwright.Route, request: playwright.Request) =>
route.fulfill({
response: await context.request.fetch(request, {
headers: {'Proxy-Authorization': 'Basic: Zm9vOmJhcgo='},
}),
}),
);
It may also work with continue
instead of fetch
/fulfill
, but I haven't tested it yet. That would look like this:
await context.route(
(_url: URL) => true,
(route: playwright.Route, request: playwright.Request) =>
route.continue({
headers: {'Proxy-Authorization': 'Basic: Zm9vOmJhcgo='},
}),
);
You could also add a check in there to make sure you're only adding Proxy-Authorization
if the request URL is for your IAP-protected app.