|
| 1 | +import type { AuthContextOutput } from "./types.d.js"; |
1 | 2 | import crypto from "node:crypto"; |
2 | 3 | import process from "node:process"; |
3 | 4 | import * as url from "node:url"; |
@@ -29,26 +30,62 @@ export default async function () { |
29 | 30 | headless: true, |
30 | 31 | executablePath: process.env.CI ? "/usr/bin/google-chrome" : undefined, |
31 | 32 | }); |
32 | | - context = await browser.newContext(); |
| 33 | + context = await browser.newContext({ |
| 34 | + // Try HTTP Basic authentication |
| 35 | + httpCredentials: { |
| 36 | + username, |
| 37 | + password, |
| 38 | + }, |
| 39 | + }); |
33 | 40 | page = await context.newPage(); |
34 | 41 |
|
35 | | - // Log in |
| 42 | + // Navigate to login page |
36 | 43 | core.info("Navigating to login page"); |
37 | 44 | await page.goto(loginUrl); |
38 | | - core.info("Filling username"); |
39 | | - await page.getByLabel(/username/i).fill(username); |
40 | | - core.info("Filling password"); |
41 | | - await page.getByLabel(/password/i).fill(password); |
42 | | - core.info("Logging in"); |
43 | | - await page |
44 | | - .getByLabel(/password/i) |
45 | | - .locator("xpath=ancestor::form") |
46 | | - .evaluate((form) => (form as HTMLFormElement).submit()); |
47 | 45 |
|
48 | | - // Write authenticated session state to a file and output its path |
49 | | - await context.storageState({ path: sessionStatePath }); |
50 | | - core.setOutput("session_state_path", sessionStatePath); |
51 | | - core.info(`Wrote authenticated session state to ${sessionStatePath}`); |
| 46 | + // Check for a login form. |
| 47 | + // If no login form is found, then either HTTP Basic auth succeeded, or the page does not require authentication. |
| 48 | + core.info("Checking for login form"); |
| 49 | + const [usernameField, passwordField] = await Promise.all([ |
| 50 | + page.getByLabel(/username/i).first(), |
| 51 | + page.getByLabel(/password/i).first(), |
| 52 | + ]); |
| 53 | + const [usernameFieldExists, passwordFieldExists] = await Promise.all([ |
| 54 | + usernameField.count(), |
| 55 | + passwordField.count(), |
| 56 | + ]); |
| 57 | + if (usernameFieldExists && passwordFieldExists) { |
| 58 | + // Try form authentication |
| 59 | + core.info("Filling username"); |
| 60 | + await usernameField.fill(username); |
| 61 | + core.info("Filling password"); |
| 62 | + await passwordField.fill(password); |
| 63 | + core.info("Logging in"); |
| 64 | + await page |
| 65 | + .getByLabel(/password/i) |
| 66 | + .locator("xpath=ancestor::form") |
| 67 | + .evaluate((form) => (form as HTMLFormElement).submit()); |
| 68 | + } else { |
| 69 | + core.info("No login form detected"); |
| 70 | + // This occurs if HTTP Basic auth succeeded, or if the page does not require authentication. |
| 71 | + } |
| 72 | + |
| 73 | + // Output authenticated session state |
| 74 | + const { cookies, origins } = await context.storageState(); |
| 75 | + const authContextOutput: AuthContextOutput = { |
| 76 | + username, |
| 77 | + password, |
| 78 | + cookies, |
| 79 | + localStorage: origins.reduce((acc, { origin, localStorage }) => { |
| 80 | + acc[origin] = localStorage.reduce((acc, { name, value }) => { |
| 81 | + acc[name] = value; |
| 82 | + return acc; |
| 83 | + }, {} as Record<string, string>); |
| 84 | + return acc; |
| 85 | + }, {} as Record<string, Record<string, string>>), |
| 86 | + }; |
| 87 | + core.setOutput("auth_context", JSON.stringify(authContextOutput)); |
| 88 | + core.debug("Output: 'auth_context'"); |
52 | 89 | } catch (error) { |
53 | 90 | if (page) { |
54 | 91 | core.info(`Errored at page URL: ${page.url()}`); |
|
0 commit comments