Puppeteer - How to manage a basic authentication prompt that pops up after clicking a link

I need assistance with handling a basic authentication popup using puppeteer=5.2.1. My specific scenario is outlined below.

1. Start an application
2. Click on a button on the home page
3. This action opens a new window (saml) requesting email input

Within the new window,
4. Input email address
5. Click on Next button
6. A basic authentication popup appears (=> assistance needed here)

The following code reaches up to the 3rd step, where I am able to access the reference of the 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 the different approaches I have tried to handle the basic auth popup in the 6th step.

Option-1: utilizing page.authenticate()

await samlPage.authenticate({username: `${myuser}`, password: `${mypass}`});
await samlPage.type(samlSignInUserTextField, `${myuser}`);
await samlPage.click(samlSignInNextButton);

=> The loading continues and the next page is not displayed.

Option-2: employing 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);

=> The basic authentication popup persists.

Option-3: Attempted to manage the auth popup using page.keyboard().

await samlPage.keyboard.type("<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="780d0b1d0a381c1715191116561b1715">[email protected]</a>");
await samlPage.keyboard.press("Tab");
await samlPage.keyboard.type("mypassword");
await samlPage.keyboard.press("Enter");

=> Unfortunately, no text input is recognized in the fields.

If anyone can offer guidance on this matter, it would be greatly appreciated.

Answer №1

We encountered a similar issue where we needed to exchange SSO username and password for an Okta Access Token within a federated setup involving OKTA->ADFS->OKTA. Our solution involved a complex flow utilizing an Okta Application, Node.js server, and Node.js client. Below is the Node.js client that achieves the following:

  • Initiates a puppeteer session
  • Submits username/password
  • Follows all redirects
  • Handles Basic HTTP Authentication dialog when it appears after a redirect

This client works seamlessly 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 all Chrome Requests
    await client.send('Network.setRequestInterception', {
        patterns: [{urlPattern: '*'}],
    })

    let authChallengeSubmitted = {}
    await client.on('Network.requestIntercepted', async e => {
        console.log(`EVENT INFO: id=${e.interceptionId}\t type=${e.resourceType}\t isNav=${e.isNavigationRequest}\t isAuthChallenge=${e.authChallenge}`)

        if (e.authChallenge) {
            if (authChallengeSubmitted[e.interceptionId]) {
                delete authChallengeSubmitted[e.interceptionId]
                await client.send('Network.continueInterceptedRequest', {
                    'interceptionId': e.interceptionId,
                    'authChallengeResponse': {
                        'response': 'CancelAuth'
                    }
                })
            } else {
                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 {
            await client.send('Network.continueInterceptedRequest', {
                interceptionId: e.interceptionId,
            })
        }
    })
}

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})

    await page.goto(loginUrl, {waitUntil: 'networkidle0'})
    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)

    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})

Answer №2

One issue I encountered was the inability to read the page URL when the authentication popup appeared. To work around this, I discovered a method that allowed me to access and manipulate the URL to send basic credentials in the format of https://user:password@domain.

var user=encodeURIComponent(`${process.env.vault_user}`)
var password=encodeURIComponent(`${process.env.password}`)

// Simulate clicking on a button on the homepage that opens a SAML window and obtain a reference to it.
var samlPage = await pagehelpers.clickAndWaitForTarget(authSubmitButton) 

// Perform actions within the SAML window
await pagehelpers.enterText(samlSignInUserTextField, `${process.env.vault_user}`, samlPage)
await pagehelpers.clickElement(samlSignInNextButton, samlPage) // This will trigger the authentication 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;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Generate a JSON object specifically for the modified input fields upon submitting the form

Is there a way to generate a JSON object only with the input fields that have been modified before submitting the form? How can I capture and store each changed value in the form as JSON data? $(document).ready(function() { $('#save').clic ...

Arrow icon from Material UI for a smaller device

As I work on coding a tab bar, I have encountered an issue where the indicator arrow button () is not displaying when the width of the tab bar goes below 600px. I would like it to appear like this: https://i.stack.imgur.com/PDum9.png However, currently i ...

Invoking a function passed via props that utilizes react-router's features

I'm really struggling to grasp this problem. Is there anyone here who could help me out? I have a component where I pass a method called this.fetchContent as props named Filter. This method triggers an action creator that uses axios with Redux to fetc ...

Ways to have MongoDB present nested JSON data as an array?

I recently came across some interesting data: { "_id" : ObjectId("5461e16ee7caf96f8f3584a2"), "num_marcacao" : "100", "sexo" : "Fêmea", "idade" : "20", "bigdata" : { "abortos" : [ { "data_aborto" : ...

Conceal virtual keyboard on mobile when in autocomplete box focus

I would like the keyboard to remain hidden when the autocomplete box is focused or clicked, and only appear when I start typing. The code below currently hides the keyboard when any alphabets or numbers are pressed. However, I want the keyboard to be hidd ...

``Can you provide guidance on excluding matching values from a dictionary object in a Angular project?

I've developed a function that takes a dictionary object and matches an array as shown below: const dict = { CheckAStatus: "PASS", CheckAHeading: "", CheckADetail: "", CheckBStatus: "FAIL", CheckBHeading: "Heading1", CheckCStatus: "FAIL", ...

What is the best way to accurately establish a new name for the evolving scope

var tags_offset=[]; $scope.getRelations = function(id, ref, subRef=0){ tags_offset[ref+'-'+subRef]=0; $http.get( CONS.appHttp+ '/tags.php?ID='+id +'&ref='+ref +'&contentType='+subRe ...

What is the most effective way to send multiple values through the <option> value attribute?

Currently, I am in the process of creating an online shopping item page. To display all the necessary information about an item (such as price, image, name, etc.), I use the 'item_id' to loop through a database containing item info. Additionall ...

Exploring Node.js and JSON: Retrieving specific object attributes

Utilizing ExpressJS, NodeJS, and Bookshelf.js In an attempt to display a user's list of friends, encountering the error "Unhandled rejection TypeError: Cannot read property 'friends' of undefined" when trying to access a property of the obj ...

Issue encountered when calling theme.breakpoints.down('') function from Material UI

As a novice, I have ventured into using material UI on the front-end of my project. My aim is to achieve responsiveness by leveraging theme.breakpoints.down as indicated in the material UI documentation. However, when attempting to implement this, I encoun ...

Interpolating strings in a graphQL query

Exploring the world of Gatsby and its graphQL query system for asset retrieval is a fascinating journey. I have successfully implemented a component called Image that fetches and displays images. However, I am facing a challenge in customizing the name of ...

Tips for creating a responsive tab indicator in Material UI?

I successfully integrated react router with material-ui and the routing system is working as expected. Clicking on a tab routes you to the corresponding component. However, I am facing an issue where the blue underline indicator that typically accompanies ...

Adding click functionality to dynamically generated list items in jQuery and HTML

I'm encountering an issue while trying to assign click events to dynamically added HTML elements in jQuery. Despite extensive research within this community, I find myself more confused than before. Below is the snippet of code causing me trouble: v ...

Implementing a Response to an AJAX POST Request with Servlets

I have a unique situation where I need to validate a username input in a text box. The process involves sending the entered username to a server for checking if it has already been taken by another user. If the username is available, I want to change the b ...

Is there a standard event triggered upon the closing of a browser tab or window?

Does React have a built-in event that is triggered when a browser tab or window is closed? If it does, does it have support across different browsers? ...

Establish and define the global variable "Protractor"

In order to pass a string value from the function editPage.CreateEntity();, you can use that value in multiple test cases by assigning it to the variable entityName. You are able to retrieve the value by setting up the test case as follows: fit('Te ...

Discovering the method for accessing a variable within jQuery from a regular JavaScript function

As someone new to jQuery, I am currently facing a challenge with accessing a variable defined inside a jQuery block from a regular function. Despite my attempts, I have been unsuccessful in accessing it. Can anyone guide me on how to do this? <script l ...

Incorporating VueJS with a sleek material design aesthetic

Currently, I am in the process of developing a web application using VueJs and am in need of a CSS framework to aid in designing without starting from scratch! After some research, I came across material-design-lite (www.getmdl.io) but unfortunately faced ...

The console is displaying a null value outside of the block, however, the correct value is returned when

I am having an issue where the first console.log() is not returning the correct value, but the second one is. Can anyone provide assistance with this problem? angular.module('resultsApp', ['ngCookies']) .config(['$qProvider&a ...

An error has occurred in the Next.js App: createContext function is not defined

While developing a Next.js application, I keep encountering the same error message TypeError: (0 , react__WEBPACK_IMPORTED_MODULE_0__.createContext) is not a function every time I try to run my app using npm run dev. This issue arises when attempting to co ...