Reputation: 121
I am looking for a solution to handle basic authentication popup (with puppeteer=5.2.1). My scenario is as below.
1. Launch an application
2. Click on a button on the home page
3. It opens a new window (saml) which ask us to enter email
In new window,
4. Enter email
5. click on Next button
6. Basic authentication popup appears (=> here I need help to handle the popup)
Here is the code to reach until 3rd step. I am able to get reference to new page (saml window)
var browser = await puppeteer.launch({
headless: false, args: ["--start-maximized"]
});
var page = await this.browser.newPage();
await waitUntilElementIsVisible(authMethodDropDown);
await wait(5000);
await page.select(authMethodDropDown, "myoption");
const samlPage = await clickAndWaitForTarget(page, authSubmitButton);
wait(5000);
await samlPage.waitForSelector(samlSignInUserTextField);
Below are various options I tried to handle basic auth popup in 6th step.
Option-1: using page.authenticate()
await samlPage.authenticate({username: `${myuser}`, password: `${mypass}`});
await samlPage.type(samlSignInUserTextField, `${myuser}`);
await samlPage.click(samlSignInNextButton);
=> It continued to load and next page is not displayed.
Option-2: using setExtraHttpHeaders
const headers = new Map();
headers.set(
'Autorization',
`Basic ${new Buffer(`${myuser}:${mypass}`).toString('base64')}`
);
headers.set('WWW-Authenticate','Negotiate');
headers.set('WWW-Authenticate','NTLM');
await samlPage.setExtraHTTPHeaders(headers);
=> Still basic authentication popup appears.
Option-3: Tried to handle auth popup using page.keyboard().
await samlPage.keyboard.type("[email protected]");
await samlPage.keyboard.press("Tab");
await samlPage.keyboard.type("mypassword");
await samlPage.keyboard.press("Enter");
=> But it is not typing anything into the fields.
Can someone please help me on this?
Upvotes: 2
Views: 7675
Reputation: 11
await page.authenticate({'username':'YOUR_BASIC_AUTH_USERNAME', 'password': 'YOUR_BASIC_AUTH_PASSWORD'});
credit https://dev.to/sonyarianto/puppeteer-quick-tip-how-to-do-basic-authentication-2pe7
Upvotes: -1
Reputation: 121
The problem is, I was not able to read the page url when authentication popup appears. As an alternative to read the URL I saw below method using which I read the URL and manipulated it to send basic credentials in the form of https://user:password@domain.
var user=encodeURIComponent(`${process.env.vault_user}`)
var password=encodeURIComponent(`${process.env.password}`)
//click on a button on homepage which opens saml window. get page reference to the saml window.
var samlPage = await pagehelpers.clickAndWaitForTarget(authSubmitButton)
//on saml window
await pagehelpers.enterText(samlSignInUserTextField, `${process.env.vault_user}`, samlPage)
await pagehelpers.clickElement(samlSignInNextButton, samlPage) // after this step we will get auth popup
const ssoRequestLS = await samlPage.waitForRequest(request => request.url().startsWith('https://sso.test.com/adfs/ls/wia'), )
modifiedURL = ssoRequestLS.url().replace(
"sso.test.com/adfs/ls/wia",
`${user}:${password}@sso.test.com/adfs/ls/wia`,
)
await pagehelpers.navigateToURL(modifiedURL, samlPage)
async clickAndWaitForTarget(locator, page = this.page) {
const pageTarget = page.target();
await page.click(locator);
const newTarget = await this.browser.waitForTarget(target => target.opener() === pageTarget);
const newPage = await newTarget.page();
await newPage.waitForNavigation({ waitUntil: "domcontentloaded" });
await newPage.waitForSelector("body");
return newPage;
}
Upvotes: 0
Reputation: 1272
We had a very similar issue: needed to exchange SSO username/password for Okta Access Token from federated setup OKTA->ADFS->OKTA. While the full flow is quite elaborated and consist of Okta Application, Node.js server and Node.js client, you can find below the Node.js client that:
Please note that it works in both "headless" and "headed" modes.
'use strict'
const puppeteer = require('puppeteer')
const loginUrl = `http://localhost:8998/login`
async function interceptAuthChallenge(client, username, password) {
// registering interception of the ALL Chrome Requests
await client.send('Network.setRequestInterception', {
patterns: [{urlPattern: '*'}],
})
let authChallengeSubmitted = {}
await client.on('Network.requestIntercepted', async e => {
// console.log(e.request.url)
console.log(`EVENT INFO: id=${e.interceptionId}\t type=${e.resourceType}\t isNav=${e.isNavigationRequest}\t isAuthChallenge=${e.authChallenge}`)
if (e.authChallenge) {
// There was an authChallenge!
if (authChallengeSubmitted[e.interceptionId]) {
// this authChallenge was already tried and failed
delete authChallengeSubmitted[e.interceptionId]
// Cancel this authChallenge
await client.send('Network.continueInterceptedRequest', {
'interceptionId': e.interceptionId,
'authChallengeResponse': {
'response': 'CancelAuth'
}
})
} else {
// Memorize that this challenge was tried; otherwise, if it fails, this authChallenge will be received again
authChallengeSubmitted[e.interceptionId] = true
console.log('responding to SSO with username/password')
await client.send('Network.continueInterceptedRequest', {
'interceptionId': e.interceptionId,
'authChallengeResponse': {
'response': 'ProvideCredentials',
'username': username,
'password': password
}
})
}
} else {
// No authChallenge - continue with the request.
await client.send('Network.continueInterceptedRequest', {
interceptionId: e.interceptionId,
})
}
// console.log()
})
}
async function loginWithCDP(username, password) {
const browser = await puppeteer.launch({
args: [
'--disable-setuid-sandbox',
'--no-sandbox',
'--ignore-certificate-errors',
],
ignoreHTTPSErrors: true,
headless: true
})
const context = await browser.createIncognitoBrowserContext()
const page = await context.newPage()
await page.setViewport({width: 1200, height: 720})
// phase 1: we are redirected to SSO routing page, where username is entered
await page.goto(loginUrl, {waitUntil: 'networkidle0'}) // wait until page load
await page.type('#id-of-the-username-field', username)
const client = await page.target().createCDPSession()
await client.send('Network.enable')
await interceptAuthChallenge(client, username, password)
// phase 2: right after the "submit" button is pressed
// another redirect is being issued from the SSO which results
// in the modal dialog with Basic HTTP Authentication
// this is handled by the *interceptAuthChallenge* function
await Promise.all([
page.click('#id-of-the-submit-button'),
page.waitForNavigation({waitUntil: 'networkidle0'}),
])
await page.waitForTimeout(4000)
await browser.close()
console.log('Done.')
}
loginWithCDP({YOUR_USERNAME}, {YOUR_PASSWORD})
Upvotes: 2