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

Is there a way to transfer PHP user information (session) to a Node.js server?

Currently, I have two separate pages set up for my website. One is the Chat page, which operates on Node.js and Socket.io using WebSockets. The other is the main page where users login and access various features. I am looking to implement a system where, ...

Arrangement of Bootstrap card components

Each card contains dynamic content fetched from the backend. <div *ngFor="let cardData of dataArray"> <div class="card-header"> <div [innerHtml]="cardData.headerContent"></div> </div> <d ...

React Higher Order Components (HOCs) are functioning correctly with certain components but not

I have encountered an issue where using a Higher Order Component (HOC) to bind an action to various types of elements, including SVG cells, results in unintended behavior. When I bind the onClick event handler normally, everything works fine, but when I ap ...

Validator returns undefined when expressing invalid data

Having an issue with validation, here is the code snippet: routes.js var express = require('express'); var router = express.Router(); var hello_controller = require('../api/controllers/helloController'); var { validationRules, validat ...

Preventing an event from bubbling up to its parent in a jQuery Ajax call: A step-by-step guide

I am working on a page that showcases a tree list using unordered lists. Each li element with children triggers an ajax call to fetch those children and display them as another ul/li combination, which is functioning correctly. However, the problem arise ...

Displaying the number of tasks completed compared to the total number of tasks within a JavaScript ToDo list

Currently, I'm in the process of creating a basic ToDo list using HTML, JS, and CSS. The last task on my list is to display to the user the total number of tasks and how many have been completed. For instance, if there are 3 completed tasks out of 7 i ...

How can I save a PDF file using React?

My webpages contain several tables, and I am looking to add a dropdown feature that allows users to choose between saving the table in either PDF or Excel format. These tables are dynamically generated with the header structured in array form and data sto ...

Add AngularJS to an AJAX call when the user clicks on it

I'm currently exploring the SoundCloud API and looking to implement a feature where users can add a song to the page by clicking on it from the search results. I've decided to utilize Plangular, which you can find more information about here. How ...

Passing this as a function parameter in Jquery

HTML <input type="button" id="1" class="add" value="+" title="Add" onclick="set(this);"/> JS function set(obj){ alert(obj.id); } The code snippet provided above seems to have an issue. My Requirement I am looking for a solution that allows ...

Unable to access external library using browserify and debowerify

I'm facing a dilemma with my current setup as I'm dealing with a headache. Here's how things are currently configured: Utilizing bower to acquire vendor libraries (specifically angular) Executing gulp tasks to run browserify Implementing d ...

The REST request is preventing the JavaScript on the page from executing

Utilizing REST to POST data through Firefox's Poster tool and encountering a url: http://[ip]/page.jsp?paramater1=whatever&parameter2=whatever (Content Type: application/x-www-form-urlencoded) The page.jsp includes: <body onload="onload()"&g ...

The pagination feature in DataTable does not retain the edited page after making changes

I have been encountering an issue while using DataTable serverside processing. My datatable includes an edit column, and when the edit link is clicked, a jQuery dialog box appears. After submitting the dialog box, an ajax.reload call is made. However, the ...

Transmit data from an HTML form to PHP using JSON

I am attempting to utilize JavaScript to send POST data. I have the data in an HTML form: <form name="messageact" action=""> <input name="name" type="text" id="username" size="15" /> <input name="massage" type="text" id="usermsg" si ...

What is the best way to create a sleek typewriter effect transition by hovering between tabs?

When hovering over a tab, the content in the main content area's child div should be displayed from a sibling div that holds the content. Initially, the details div style is set to hide the contents but upon hover, it is displayed. The described func ...

The useEffect hook in my React app causes the homepage to refresh

Currently, I am facing a challenge in retrieving user information from the Redux state to display on the homepage after a user signs in. The issue arises when the component refreshes and all the data stored in Redux gets lost due to the useEffect hook im ...

Instead of only one menu icon, now there are three menu icons displayed on the screen. (Additionally, two more divs containing

When visiting on a smartphone browser, you may notice that instead of one menu icon, three icons appear. Upon inspecting the elements, you will find that there are three div's that look like this: <div class="responsive-menu-icon">&l ...

Cross-Origin Resource Sharing Problem - Angular version 8 with NodeJS and ExpressJS

I've attempted various solutions from different sources and have a feeling that I may be overlooking something minor here. In my setup, I have an AngularJS 8 application running on Node 10 with ExpressJS. The specific issue I'm encountering rela ...

Tips for altering the class of an HTML button dynamically when it's clicked during runtime

I currently have a set of buttons in my HTML page: <button id="button1" onclick="myFunction()" class="buttonClassA"></button> <button id="button2" onclick="myFunction()" class="buttonClassA"></button> <button id="button3" onclic ...

Tips on concealing confidential keys within a React.js application on the frontend aspect

Desperate times call for desperate measures - I am attempting to conceal a secret key within my React frontend application. While I understand the risks involved, I feel compelled to do so without any other viable options. The reason behind my actions is ...

The Promise.all function encountered an error: Uncaught TypeError: #<Promise> is not an iterable object

Currently, I am dealing with the challenge of hitting two APIs and waiting for both responses to return before dispatching my action. Although I am utilizing Promise.all, I am encountering the following error: index.js:51 Uncaught (in promise) TypeErro ...