Skip to content

Commit

Permalink
feat(authenticate): Provide credentials for HTTP authentication(redo)…
Browse files Browse the repository at this point in the history
… && Fix "No usable sandbox!" with user namespace cloning enabled
  • Loading branch information
ahuigo committed Jan 13, 2025
1 parent 3a90da3 commit df1003a
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 6 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ jobs:
with:
deno-version: v2.x

- name: Disable AppArmor
if: ${{ matrix.os == 'ubuntu-latest' }}
run: echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns

- name: check format
if: matrix.os == 'ubuntu-latest'
run: deno fmt --check
Expand Down
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,16 @@ const browser = await launch();
const anotherBrowser = await launch({ wsEndpoint: browser.wsEndpoint() });
```

### Page authenticate

[authenticate example code](https://github.com/lino-levan/astral/blob/main/examples/authenticate.ts):

// Open a new page
const page = await browser.newPage("https://httpbin.org/basic-auth/user/passwd");

// Provide credentials for HTTP authentication.
await page.authenticate({ username: "user", password: "passwd" });

## BYOB - Bring Your Own Browser

Essentially the process is as simple as running a chromium-like binary with the
Expand Down Expand Up @@ -175,3 +185,24 @@ console.log(await page.evaluate(() => document.title));
// Close connection
await browser.close();
```

## FAQ

### Launch FAQ

#### "No usable sandbox!" with user namespace cloning enabled

> Ubuntu 23.10+ (or possibly other Linux distros in the future) ship an AppArmor
> profile that applies to Chrome stable binaries installed at
> /opt/google/chrome/chrome (the default installation path). This policy is
> stored at /etc/apparmor.d/chrome. This AppArmor policy prevents Chrome for
> Testing binaries downloaded by Puppeteer from using user namespaces resulting
> in the No usable sandbox! error when trying to launch the browser. For
> workarounds, see
> https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md
The following shell can remove AppArmor restrictions on user namespaces so that
Puppeteer can launch Chrome without the "No usable sandbox!" error (Refer
[puppeteer#13196](https://github.com/puppeteer/puppeteer/pull/13196)):

echo 0 | sudo tee /proc/sys/kernel/apparmor_restrict_unprivileged_userns
24 changes: 24 additions & 0 deletions examples/authenticate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/// <reference lib="dom" />

// Import Astral
import { launch } from "../mod.ts";

// Launch the browser
const browser = await launch();

// Open a new page
const page = await browser.newPage();

// Provide credentials for HTTP authentication.
await page.authenticate({ username: "postman", password: "password" });
const url = "https://postman-echo.com/basic-auth";
await page.goto(url, { waitUntil: "networkidle2" });

// Get response
const content = await page.evaluate(() => {
return document.body.innerText;
});
console.log(content);

// Close the browser
await browser.close();
34 changes: 28 additions & 6 deletions src/page.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { deadline } from "@std/async/deadline";
import { fromFileUrl } from "@std/path/from-file-url";

import { Celestial } from "../bindings/celestial.ts";
import type {
Fetch_requestPausedEvent,
Network_Cookie,
Runtime_consoleAPICalled,
} from "../bindings/celestial.ts";
import { Celestial } from "../bindings/celestial.ts";
import type { Browser } from "./browser.ts";
import { ElementHandle } from "./element_handle.ts";
import { convertToUint8Array, retryDeadline } from "./util.ts";
import { Mouse } from "./mouse.ts";
import { Keyboard } from "./keyboard.ts";
import { Touchscreen } from "./touchscreen.ts";
import { Dialog } from "./dialog.ts";
import { ElementHandle } from "./element_handle.ts";
import { FileChooser } from "./file_chooser.ts";
import { Keyboard } from "./keyboard.ts";
import { Locator } from "./locator.ts";
import { Mouse } from "./mouse.ts";
import { Touchscreen } from "./touchscreen.ts";
import { convertToUint8Array, retryDeadline } from "./util.ts";

/** The options for deleting a cookie */
export type DeleteCookieOptions = Omit<
Expand Down Expand Up @@ -286,6 +286,28 @@ export class Page extends EventTarget {
return this.#celestial;
}

/**
* Provide credentials for HTTP authentication.
*
* @example
* ```ts
* await page.authenticate({ 'username': username, 'password': password });
* ```
*/
authenticate(
{ username, password }: { username: string; password: string },
): Promise<void> {
function base64encoded(s: string) {
const bytes = new TextEncoder().encode(s);
return btoa(String.fromCharCode(...bytes));
}

const auth = base64encoded(`${username}:${password}`);
return this.#celestial.Network.setExtraHTTPHeaders({
headers: { "Authorization": `Basic ${auth}` },
});
}

/**
* Runs `document.querySelector` within the page. If no element matches the selector, the return value resolves to `null`.
*
Expand Down
29 changes: 29 additions & 0 deletions tests/authenticate_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/// <reference lib="dom" />

import { assertEquals, assertNotEquals } from "@std/assert";

import { launch } from "../mod.ts";

Deno.test("Testing authenticate", async (_t) => {
// Open the webpage
const browser = await launch({ headless: true });
const page = await browser.newPage();

// Provide credentials for HTTP authentication.
await page.authenticate({ username: "postman", password: "password" });
const url = "https://postman-echo.com/basic-auth";
await page.goto(url, { waitUntil: "networkidle2" });

// Get JSON response
const content = await page.evaluate(() => {
return document.body.innerText;
});

// Assert JSON response
assertNotEquals(content, "");
const response = JSON.parse(content);
assertEquals(response.authenticated, true);

// Close browser
await browser.close();
});

0 comments on commit df1003a

Please sign in to comment.