New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feature] allow client certificate selection and settings from Javascript #1799
Comments
You can apply same workaround as for puppeteer. Secondly you can inject dynamically an certificate as is =>
|
Hey @yyvess do you have an example of how remove client certificate programmatically? |
Hi @gepd, as I know you cannot remove it programmatically. But if you have only one certificate installed, the browser should not ask you to select one. Note that the popup is not present when you run your test in background. |
Hey folks! We are currently evaluating and digging into this feature and have a few questions so it will fit your needs:
Thank you! ❤️ And sorry for the ping. cc @osmenia, @SMN947, @delsoft, @dcr007, @inikulin, @BredSt, @matthias-ccri, @nlack, @sdeprez, @bramvanhoutte, @yyvess, @gepd |
👋 great news!
|
Hi, Thanks for your interest on this features ! On my case applications use client certificate to do the authentication of users. Today as work around we inject certificate as I described before on this thread.
Will more easy to pass a list (or map<key,file>) of certificate files to Playwright during the initialization.
Converting certificate is not an issue.
We have some test that require different users with different privilege.
Not really needed |
Hey! Thanks for considerate this! in my use case:
Our use case; we need to download a file from a gov website, this website only works creating a session with a PFX certificate. Each user doesn't need to download that file very often, but we have many users, that is why on the fly loading is good for our use case Thanks again! |
The solution @yyvess proposed works for most use cases. I have an issue with multipart POST request, like file uploads. |
I'd like to add my thoughts to this discussion: Is selecting a certificate out of the given ones from the operating system programatically enough? (like e.g. a clientCertificate event where you can select out of the loaded ones or a mapping from hosts to client certificates) Do you want to load a custom certificate manually or is it already in your operating system certificate storage? If you add them manually, are you using PEM or PFX certificate file format? Is it enough if its on browser.launch level? (context level with multiple certificates on each context would be the alternative) Do you want to validate against a CA? (your use-case would also help a lot) We use Playwright in two ways: E2E tests and automated scripts. Current solution for E2E is ok, but automated scripts runtime is somewhat problematic. Our intention is to write scripts with APIs as much as possible, however, some of our legacy apps do not have these. In that case, we use playwright as an workaround. All of these legacy apps are behind HTTPS (sometimes with certs issued by local CA) and client certs auth. One more important factor is Docker. We run E2E tests with Gitlab CI runner that uses docker containers. We would also like to run these scripts in docker containers as you do not need to install node, playwright, no updates required. This is why asking OS for cert is not sufficient. PS. Using custom TLS certs for APIs is not a problem at all. |
Hi @mxschmitt, I've raised on the dotnet repo the issue of not being able to import certificates or to use already-existing certificates in headless mode (microsoft/playwright-dotnet#1601). That issue got merged into this one, which is when I started having some concerns that this somewhat unrelated issue would be bundled with the actual problem that I've raised on the dotnet repo. For my organisation, certificate selection is a nice-to-have, and this can be easily worked around via the registry in Windows and via config on UNIX-based systems, in lieu of programmatic means, however not being able to import certificates in dotnet or to use already-existing certificates in headless mode is currently a blocker on one of our products. Hope this information helps towards development on this; for me and my organisation, this would be the most important feature since the first release on the stable channel. Cheers. |
This is also our use case, including docker:
|
If you came here looking for a solution and you saw the solution from @yyvess, you may also be looking for where to implement that solution. It took me a while trying to integrate it in the global config, or even a setup/teardown file, but I just couldn't find the correct apis that would let me do that. So I finally landed on creating a custom fixture that provides a context and then also re-exports all of I have a global config that I'll include for completeness, but it really doesn't do anything special. The real magic is in the fixture which is used by the test for all of the Playwright imports. Note, if you need to do MFA Authentication and want to use the documented persistent context, but can't figure out how to tell playwright to launch a persistent context, you can use this same solution, instead of using the provided context in the fixture, you would just create one in the fixture and it will be provided to any tests that use the fixture. // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test'
const config: PlaywrightTestConfig = {
testDir: 'tests',
use: {
channel: 'chrome',
ignoreHTTPSErrors: true,
},
}
export default config // caAuthenticationFixture.ts
import { test as base, chromium, BrowserContext } from '@playwright/test'
import fs from 'fs'
import request, { CoreOptions } from 'request'
export const test = base.extend({
context: async ({ context }, use) => {
// I use the context that is created using my base config here, just adding the route, but you could also create
// a context first if you needed even more customizability.
await context.route('**/*', (route, req) => {
const options = {
uri: req.url(),
method: req.method(),
headers: req.headers(),
body: req.postDataBuffer(),
timeout: 10000,
followRedirect: false,
agentOptions: {
ca: fs.readFileSync('./certs/ca.pem'),
pfx: fs.readFileSync('./certs/user.p12'),
passphrase: fs.readFileSync('./certs/user.p12.passwd', 'utf8'),
},
}
let firstTry = true
const handler = function handler(err: any, resp: any, data: any) {
if (err) {
/* Strange random connection error on first request, do one re-try */
if (firstTry) {
firstTry = false
return request(options, handler)
}
console.error(`Unable to call ${options.uri}`, err.code, err)
return route.abort()
} else {
return route.fulfill({
status: resp.statusCode,
headers: resp.headers,
body: data,
})
}
}
return request(options, handler)
})
use(context)
},
})
export * from '@playwright/test' // login.spec.ts
import { test, expect } from './certAuthenticationFixture' // <-- note the import of everything from our fixture.
test('login test', async ({ page, context }) => {
await page.goto('https://my.page.net')
const title = page.locator('title')
await expect(title).toHaveText('My Title')
}) |
Hi, @fargraph: In using Node v14.18.0, were you (or anyone else) able to get beyond the Node-level (but not Node error) message In other automation frameworks that I've used, they typically had cert auth baked in, so having to rely upon Node wasn't necessarily an issue. Thanks! |
I believe that error is bypassed by this line in the playwright config: // playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test'
const config: PlaywrightTestConfig = {
testDir: 'tests',
use: {
channel: 'chrome',
ignoreHTTPSErrors: true, // <-- bypass the SELF_SIGNED_CERT_IN_CHAIN Error
},
}
export default config See this issue for reference: #2814 |
@fargraph: Unfortunately, no, This is something likely very simple that I'm simply overlooking. Appreciate the help. |
While the request is being implemented, is there a way to simply click "Cancel" in the "Select a certificate" dialog? |
We would also need just a way to cancel the popup. I am currently implementing a login tests, and it always gets stuck when the browser is asking for a certificate, that we are trying to cancel ether way. Just that playwright does not offer the possibility when using |
Hi guys, few days ago I involved on the same problem and want to share here my workaround for this. Important, this workaround is really weak and need improve to attach the real goal: select the certificate programmatically
In this repository explain better the workaround >> playwright-auto-select-certificates-for-url Basically manage the policies of the chromium browser in the path {
"AutoSelectCertificateForUrls": ["{\"pattern\":\"*\",\"filter\":{}}"]
} The browser launch read this policy and automatic select the cert for the url. Any mistake pls sorry and sorry for my english. |
Ugly may be too harsh, but they are definitely not usable in CI, as they only concern headed mode, and not every browser. Plus it would permit easier multiple authentication. |
@Az8th. I agree it's straight forward to make this for a single browser in headed mode, it becomes tedious in cross browser tests on CI. And someone also mentioned this feature is already in cypress. I will definitely give that a try. |
I'm really sorry to inform you that after a whole month of searching for a workaround, I'm moving to Cypress... |
Cypress is not the better option. Please have a look into nightwatch much
more software developer like.
Ilor ***@***.***> schrieb am So., 19. Nov. 2023, 15:31:
… I'm really sorry to inform you that after a whole month of searching for a
workaround, I'm moving to Cypress...
We have about 15 Automation projects that I wanted to migrate from
Selenium/Cypress to Playwright.
My own project which is the one that should lead the whole migration
failed to workaround this auth-related obstacle.
I'm very disappointed because I really wanted to lead this thing, and this
is a rare opportunity in such a big company, not to mention that the whole
company stack is based on Microsoft.
—
Reply to this email directly, view it on GitHub
<#1799 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABQ7XGS233K4JRWSMR6FRX3YFIJ3BAVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRG44DOMRTGEYQ>
.
You are receiving this because you commented.Message ID:
***@***.***>
|
I have done a workaround based on the Node https agent and Axios.
sön 19 nov. 2023 kl. 15:44 skrev Andreas Hetz ***@***.***>:
… Cypress is not the better option. Please have a look into nightwatch much
more software developer like.
Ilor ***@***.***> schrieb am So., 19. Nov. 2023, 15:31:
> I'm really sorry to inform you that after a whole month of searching for
a
> workaround, I'm moving to Cypress...
> We have about 15 Automation projects that I wanted to migrate from
> Selenium/Cypress to Playwright.
> My own project which is the one that should lead the whole migration
> failed to workaround this auth-related obstacle.
> I'm very disappointed because I really wanted to lead this thing, and
this
> is a rare opportunity in such a big company, not to mention that the
whole
> company stack is based on Microsoft.
>
> —
> Reply to this email directly, view it on GitHub
> <
#1799 (comment)>,
> or unsubscribe
> <
https://github.com/notifications/unsubscribe-auth/ABQ7XGS233K4JRWSMR6FRX3YFIJ3BAVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRG44DOMRTGEYQ>
> .
> You are receiving this because you commented.Message ID:
> ***@***.***>
>
—
Reply to this email directly, view it on GitHub
<#1799 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AG7INMCAVYSYUD6R24I3DQTYFILLJAVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRG44DONJUGM4Q>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Yeah, great, except that all the language-specific issues have been aggregated into this issue, so this workaround may only be valid for TypeScript users but not for anyone else. For instance, I raised this problem for C# and it's been closed and merged into this issue, and none of these workarounds apply to me, and on top of that all the workarounds here seem to be for TypeScript because the issue title has not been updated to reflect the aggregation. |
Ok
mån 20 nov. 2023 kl. 08:06 skrev Vlad Tănăsescu ***@***.***>:
… I have done a workaround based on the Node https agent and Axios.
Yeah, great, except that all the language-specific issues have been
aggregated into this issue, so this workaround may only be valid for
TypeScript users but not for anyone else. For instance, I raised this
problem for C# and it's been closed and merged into this issue, and none of
these workarounds apply to me, and on top of that all the workarounds here
seem to be for TypeScript because the issue title has not been updated to
reflect the aggregation.
—
Reply to this email directly, view it on GitHub
<#1799 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AG7INMCMT5TMIZKLGJPHAWDYFL6Q5AVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRHAZTIOJUG4ZQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@janostgren |
The same happens to me; I have some automations using Java, but I don't have a solution for Playwright with certificate selection. Is there any workaround? |
My workaround for Chrome / FF above is at the OS level. That same method
can be used on Windows via registry changes.
…Sent from my iPhone
On Mon, Nov 20, 2023 at 12:06 AM Vlad Tănăsescu ***@***.***> wrote:
I have done a workaround based on the Node https agent and Axios.
Yeah, great, except that all the language-specific issues have been
aggregated into this issue, so this workaround may only be valid for
TypeScript users but not for anyone else. For instance, I raised this
problem for C# and it's been closed and merged into this issue, and none of
these workarounds apply to me, and on top of that all the workarounds here
seem to be for TypeScript because the issue title has not been updated to
reflect the aggregation.
—
Reply to this email directly, view it on GitHub
<#1799 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ACSDKRB3XPSXQ2NWY37MFHLYFL6QXAVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRHAZTIOJUG4ZQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
It is for a customer but we can have chat and discuss the topic.
mån 20 nov. 2023 kl. 14:36 skrev Ilor ***@***.***>:
… @janostgren <https://github.com/janostgren>
Plz, can you share it with me?
I'm using TS too, and if I'll find a full solution that would be great!
Share it here or anywhere you want, thanks for the help
—
Reply to this email directly, view it on GitHub
<#1799 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AG7INMGVEM65P4MZCQJIGV3YFNMEJAVCNFSM4MIV2WZ2U5DIOJSWCZC7NNSXTN2JONZXKZKDN5WW2ZLOOQ5TCOBRHEYDONZTGU4Q>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
@janostgren |
Hey community. Any news regarding this feature? It is really block me to use Playwright |
Workaround (UI)Hey guys, I've posted a question about this issue on StackOverFlow,
|
@alexyablonskyi Check my previous commend, hope you'll find something useful |
I've had similar issue and as a workaround using axios and fixture of playwright httpClient.js
subscription.js
test-extend.js
api-test.spec.js
Done |
Thanks for this workaround @IgorZn |
No, it doesn't work for UI |
I also need this feature. I don't want it in the cypress way, I wanna select certificate programmatically e.g. by name in each test 😤. |
since nothing happened. We proceed with Robotframework. |
Hello(。・ω・。)ノI've managed to implement and find a solution using F# (.NET) instead of JavaScript. let private httpRequestAsync (clientCert: X509Certificate2) (request: IRequest) =
Http.AsyncRequest(
url = request.Url
, httpMethod = request.Method
, headers = (request.Headers |> Seq.map(fun x -> x.Key,x.Value))
, customizeHttpRequest =
fun req ->
req.ClientCertificates.Add(clientCert) |> ignore
req
)
let private routeResponseWithCertificateAsync (clientCert: X509Certificate2) (route:IRoute) =
task {
try
let! httpResponse = httpRequestAsync clientCert (route.Request)
let opt = new RouteFulfillOptions()
opt.Headers <- httpResponse.Headers |> Map.toSeq |> Seq.map(fun (k,v) -> KeyValuePair(k,v))
opt.BodyBytes <-
match httpResponse.Body with
| Text text -> System.Text.Encoding.UTF8.GetBytes(text)
| Binary bytes -> bytes
opt.ContentType <- httpResponse.Headers.Item("Content-Type")
opt.Status <- httpResponse.StatusCode |> Nullable
do! route.FulfillAsync(opt)
with _ ->
do! route.AbortAsync()
}
:> Task
[<EntryPointAttribute>]
let main _ =
task {
let url = "https://foo..."
let urlPattern = "**/bar/"
let clientCert =
let cn = "baz"
Certfs.GetCertificateByCommonName cn Certfs.myStoreName.My
let! browser = Playwright.CreateAsync()
let! edge = browser.Chromium.LaunchAsync(BrowserTypeLaunchOptions(Channel="msedge",Headless=false))
let! context = edge.NewContextAsync()
do! context.RouteAsync(urlPattern,(routeResponseWithCertificateAsync clientCert))
let! page = context.NewPageAsync()
do! sample url page
}
|> Task.WaitAll
0 |
Does it work when there are multiple certificates in pop-up? I have tried something similar but the main problem that my route doesn't intercepted because my website has OIDC authentication so many redirects occure. Any workarounds? |
Does it work when there are multiple certificates in pop-up? I have tried something similar but the main problem that my route doesn't intercepted because my website has OIDC authentication so many redirects occure. Any workarounds? |
Thanks for your reply, I'll take a look. I'll publish my Java solution if I'll make it work. |
I'll add my solution again since it works perfectly and even better than Cypress built-in solution:A great workaround for Windows & Chromium users - using Windows Registry I’m demonstrating on Chromium/Chrome, but it’s the same for every other Chromium-based browser. (you just need to figure the exact key path) For the Playwright Chromium browser you need to create this registry key:
with this value pattern:
In this example I'm executing the CMD const key = "HKEY_CURRENT_USER\\SOFTWARE\\Policies\\Chromium\\AutoSelectCertificateForUrls";
const value = "1";
const data = `{\\"pattern\\":\\"${url}\\",\\"filter\\":{\\"SUBJECT\\":{\\"CN\\":\\"${certName}\\"}}}`;
exec(`reg add "${key}" /v "${value}" /d "${data}"`); Now, navigate to your site and the certificate pop-up shouldn't pop-up. |
Thanks. It works not for Windows only, but it is a not flexy solution. I build a platform with RBAC and I have about 10-20 certificates for QA stand (multiple roles). Suppose, if I use Java, I wanna create the annotation So it's about selecting certificates programmatically, wonder that it is not properly implemented by any framework. P.S. Also this policy requires browser to be run headed which is also more expensive and more difficult especially in CI. |
I think the solution using the registry is also very valuable. In fact, I was doing that too.However, not everyone can manipulate the registry.The method using F# prevents the pop-up from appearing in the first place, so I believe it has its own value. |
I'm Sorry...I mentioned in my previous post that I managed to get certificate authentication working with F# without touching the registry settings. Turns out, it was actually working because of some pre-existing registry settings I hadn't noticed. Sorry for the mix-up! Here's the code I was talking about: let private httpRequestAsync (clientCert: X509Certificate2) (request: IRequest) =
Http.AsyncRequest(
url = request.Url
, httpMethod = request.Method
, headers = (request.Headers |> Seq.map(fun x -> x.Key,x.Value))
, customizeHttpRequest =
fun req ->
req.ClientCertificates.Add(clientCert) |> ignore
req
)
let private routeResponseWithCertificateAsync (clientCert: X509Certificate2) (route:IRoute) =
task {
try
let! httpResponse = httpRequestAsync clientCert (route.Request)
let opt = new RouteFulfillOptions()
opt.Headers <- httpResponse.Headers |> Map.toSeq |> Seq.map(fun (k,v) -> KeyValuePair(k,v))
opt.BodyBytes <-
match httpResponse.Body with
| Text text -> System.Text.Encoding.UTF8.GetBytes(text)
| Binary bytes -> bytes
opt.ContentType <- httpResponse.Headers.Item("Content-Type")
opt.Status <- httpResponse.StatusCode |> Nullable
do! route.FulfillAsync(opt)
with _ ->
do! route.AbortAsync()
}
:> Task
[<EntryPointAttribute>]
let main _ =
task {
let url = "https://foo..."
let urlPattern = "**/bar/"
let clientCert =
let cn = "baz"
Certfs.GetCertificateByCommonName cn Certfs.myStoreName.My
let! browser = Playwright.CreateAsync()
let! edge = browser.Chromium.LaunchAsync(BrowserTypeLaunchOptions(Channel="msedge",Headless=false))
let! context = edge.NewContextAsync()
do! context.RouteAsync(urlPattern,(routeResponseWithCertificateAsync clientCert))
let! page = context.NewPageAsync()
do! sample url page
}
|> Task.WaitAll
0 Really sorry for sharing incorrect information earlier. This has been a good reminder of how important it is to fully understand all the elements behind a solution. Looking forward to learning more and contributing to the community. Thanks for your understanding! |
Thanks for your hard work and honesty. any kind of solution being found its a great progress. |
Similarly to puppeteer/puppeteer#540
Currently when navigating to a page that requires client certificates and client certificates are available a popup is shown in Firefox and Chrome which asks to select which certificate to use. It would be beneficial to provide an API to select the correct certificate to use (or use none).
The text was updated successfully, but these errors were encountered: