Skip to main content

Playwright — Automating HTTP Basic Auth (Browser Login Dialog)

Divya Manohar
Co-Founder and CEO, DevAssure

HTTP Basic Authentication is a simple authentication scheme built into the HTTP protocol. It prompts users with a browser dialog box to enter their username and password before granting access to a web resource. Unlike standard web login forms that are part of the webpage, Basic Auth uses a browser-level popup, which can be tricky to automate in end-to-end testing frameworks like Playwright.

In this article, we will explore how to handle HTTP Basic Auth in Playwright tests effectively.

What is HTTP Basic Authentication?

HTTP Basic Authentication works by sending the username and password encoded in the HTTP headers. When a user tries to access a protected resource, the server responds with a [401 Unauthorized](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/401) status code and a WWW-Authenticate header, prompting the browser to display a login dialog.

WWW-Authenticate: Basic realm="User Visible Realm"

The browser then shows a native popup dialog asking for credentials. This will not be part of the HTML DOM, making it different from typical login forms.

Why page.fill() will NOT work here

The page.fill() method in Playwright is designed to interact with elements in the DOM. However, since the HTTP Basic Auth dialog is a native browser popup, it is not part of the DOM and cannot be accessed or manipulated using standard Playwright methods like page.fill().

The same applies for other methods like page.type(), page.click(), etc. These methods will not work for the Basic Auth dialog because it is outside the scope of the webpage's DOM.

Despite the shortcomings, there is a still a way to handle HTTP Basic Auth in Playwright.

import { test, expect } from '@playwright/test';

test.use({
httpCredentials: {
username: 'admin',
password: 'admin',
},
});

test('Logs in with Basic Auth', async ({ page }) => {
await page.goto('https://the-internet.herokuapp.com/basic_auth');
await expect(page.locator('p')).toContainText('Congratulations!');
});

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
browser = p.chromium.launch()
context = browser.new_context(http_credentials={'username': 'admin', 'password': 'admin'})
page = context.new_page()
page.goto('https://the-internet.herokuapp.com/basic_auth')
print(page.text_content('body'))
browser.close()

In this method, we use Playwright's built-in httpCredentials option to provide the username and password for HTTP Basic Authentication. This way, Playwright automatically handles the authentication process when navigating to the protected resource.

With this method, when the url is loaded in the browser, Playwright will automatically send the provided credentials in the HTTP headers, allowing access to the protected resource without any manual interaction with the login dialog. In a nutshell, the browser popup is bypassed entirely.

Method 2 — Using URL with Credentials

Syntax

https://username:[email protected]

Example

import { test, expect } from '@playwright/test';
test('Logs in with Basic Auth via URL', async ({ page }) => {

await page.goto('https://admin:[email protected]/basic_auth');

await expect(page.locator('p')).toContainText('Congratulations!');

});

When to use:

  • Use this method when you need a quick and straightforward way to handle Basic Auth without modifying your test setup.
  • This approach is suitable for one-off tests or when working with a small number of protected resources.
  • Keep in mind that including credentials in the URL can expose sensitive information in logs or browser history, so use this method with caution.

When not to use:

  • Avoid this method in production environments or when dealing with sensitive data, as it can lead to security vulnerabilities.
  • Do not use this approach if you need to manage multiple sets of credentials or require more complex authentication flows.
  • If username/password contains @, :, or /. This would require URL encoding which can be cumbersome.

Handling Special Characters in Passwords

If your password contains special characters like @, :, or /, you need to URL-encode these characters to ensure they are interpreted correctly in the URL.

Solution → Encode:

import { test, expect } from '@playwright/test';
import { encodeURIComponent } from 'querystring';
test('Logs in with Basic Auth via URL with encoded password', async ({ page }) => {
const username = 'admin';
const password = 'p@ss:w/rd';
const encodedPassword = encodeURIComponent(password);
await page.goto(`https://${username}:${encodedPassword}@the-internet.herokuapp.com/basic_auth`);
await expect(page.locator('p')).toContainText('Congratulations!');
});

Or using URL API:

import { test, expect } from '@playwright/test';
test('Logs in with Basic Auth via URL with encoded password', async ({ page }) => {
const username = 'admin';
const password = 'p@ss:w/rd';
const url = new URL('https://the-internet.herokuapp.com/basic_auth');
url.username = username;
url.password = password;
await page.goto(url.toString());
await expect(page.locator('p')).toContainText('Congratulations!');
});

When It Fails — Common Gotchas

IssueReason & Solution
Credentials not workingSite uses form-based auth, not Basic Auth
Popup still appearshttpCredentials applied to wrong context
URL with creds doesn't workCredentials contain special chars → need encodeURIComponent()
Works in browser but fails in PlaywrightCookies/cache interfering → try new context

Conclusion

  • HTTP Basic Auth is easy to automate in Playwright using httpCredentials.
  • Avoid interacting with the popup — instead inject credentials before navigation.
  • URL-based auth works, but only for simple cases.
  • Use encoding when passwords contain : or @.

🚀 See how DevAssure accelerates test automation, improves coverage, and reduces QA effort.
Ready to transform your testing process?

Schedule Demo