ScreenshotNeo

BlogHow-to

How to Schedule Screenshots of a Cookie-Protected Customer Portal Without Saving Passwords

Reuse a protected browser session to capture a customer portal on a schedule without storing its password. Learn how to secure the session and detect expired logins.

By the ScreenshotNeo team4 October 20269 min read

A scheduled Playwright job can capture a cookie-protected customer portal without saving or re-entering its password on each run. Authenticate once through a permitted flow, save the browser state to a restricted location, load that state for each scheduled capture, and verify the page is authenticated before saving the screenshot. The saved state contains session credentials, so protect it like a password.

Before automating access, confirm the portal’s terms and your administrator’s policy permit it. Prefer an official API or identity-provider flow when available. MFA, CAPTCHA, session lifetime, and the portal’s storage behavior are portal-specific; Playwright cannot establish those rules for you.

1. Check the portal’s authentication and automation policy

Identify how the portal signs users in and what its approved automated-access route is. Ask the portal owner or administrator when the policy is unclear. A saved cookie or token can grant account access even when the password itself is never stored.

  • Confirm that recurring browser automation is allowed for this account and data.
  • Check whether an official API, service account, or approved identity-provider flow is available.
  • Determine whether sign-in uses cookies, local storage, IndexedDB, sessionStorage, SSO, MFA, or CAPTCHA.
  • Find out how sessions expire and what renewal requires. Do not assume a session can be renewed unattended.
  • Decide who may access session state, screenshots, logs, and retained job artifacts.

Playwright supports saving authenticated browser state and loading it into later contexts. Its authentication guide warns that the saved file can contain sensitive cookies and headers that could impersonate the account. Keep it out of source control and broadly accessible artifacts. See Playwright authentication and the BrowserContext API.

2. Install Playwright and prepare a restricted state directory

This example uses Node.js and Chromium. Run it in a private working directory on a controlled machine or runner. Install Playwright and its browser:

npm init -y
npm install playwright
npx playwright install chromium

Create a state directory and ensure it is excluded from Git. For example, add this entry to .gitignore:

playwright/.auth/

Use filesystem permissions and your runner’s secret-management controls to restrict access. The state file is a bearer credential in practice: someone who can use it may be able to act as the signed-in account until the portal invalidates it. Do not print its contents, upload it as a general build artifact, or include it in debug bundles.

3. Sign in once and save the browser state

For a portal that permits this setup, sign in interactively in a controlled browser session, then save the authenticated state. The following setup script opens a visible Chromium window, waits for you to finish the approved sign-in, and writes state to playwright/.auth/portal.json.

// save-auth.js
const { chromium } = require('playwright');
const fs = require('node:fs/promises');

(async () => {
  const browser = await chromium.launch({ headless: false });
  const context = await browser.newContext();
  const page = await context.newPage();

  await page.goto('https://portal.example.com/login');
  console.log('Complete the permitted sign-in in the opened browser.');
  await page.waitForURL('**/dashboard**', { timeout: 5 * 60 * 1000 });

  await fs.mkdir('playwright/.auth', { recursive: true });
  await context.storageState({ path: 'playwright/.auth/portal.json' });
  await browser.close();
})().catch((error) => {
  console.error('Could not save authenticated browser state:', error.message);
  process.exitCode = 1;
});

Replace the example domain and destination pattern with the portal’s real login URL and a page reached only after successful authentication. If the portal requires a different permitted setup flow, adapt this step rather than trying to bypass MFA, CAPTCHA, or access controls. Playwright also documents setting up state through an API when the application supports that route: authentication setup options.

4. Capture only after confirming authentication

Load the saved state into a new browser context for each run. Navigate to the target page, reject redirects to login, and check an authenticated-page marker before saving. Choose a marker that is specific to the signed-in portal, such as a stable account navigation element. Do not treat a successful HTTP response alone as proof that the page is authenticated.

// capture-portal.js
const { chromium } = require('playwright');
const fs = require('node:fs/promises');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const context = await browser.newContext({
    storageState: 'playwright/.auth/portal.json',
    viewport: { width: 1440, height: 1000 },
  });
  const page = await context.newPage();

  try {
    await page.goto('https://portal.example.com/customer/overview', {
      waitUntil: 'domcontentloaded',
      timeout: 45_000,
    });

    if (page.url().includes('/login')) {
      throw new Error('Portal redirected to login; saved session may have expired.');
    }

    // Replace this selector with a marker visible only to authenticated users.
    await page.locator('[data-testid="account-navigation"]').waitFor({
      state: 'visible',
      timeout: 15_000,
    });

    await page.screenshot({ path: 'portal.png', fullPage: true });
    console.log('Authenticated portal screenshot saved to portal.png');
  } finally {
    await context.close();
    await browser.close();
  }
})().catch((error) => {
  console.error('Capture failed:', error.message);
  process.exitCode = 1;
});

Run the scripts manually first and confirm the output contains the intended account and page. The capture intentionally fails without writing an image if it lands on a login page or cannot find the authenticated marker. Playwright’s Page API documents navigation and screenshots.

5. Schedule the job and control its outputs

Run the capture from a CI scheduler, container scheduler, or another approved job runner. The runner must have Node.js, Playwright’s Chromium dependencies, network access to the portal, and read access to the restricted state file. Keep the browser/runtime environment consistent when comparing captures; Playwright notes that operating system, browser version, settings, hardware, power source, and headless mode can affect screenshots. Its CI guide describes CI and container execution.

For a Linux host using cron, an example schedule that runs daily at 07:00 UTC is:

0 7 * * * cd /srv/portal-capture && /usr/bin/node capture-portal.js

Use a runner-specific secret or restricted file mount to provide playwright/.auth/portal.json; avoid checking it into the project just to make the scheduled job work. Set an explicit screenshot destination and retention policy that meet your organization’s privacy requirements. Restrict access to the image as well: portal screenshots may contain customer data. The schedule, storage destination, and retention period depend on your environment and are not prescribed by Playwright.

6. Handle session expiry and storage edge cases

Expired or revoked sessions

A session may expire, be revoked, or require interactive renewal. If the login redirect or authenticated-marker check fails, mark the run as failed and notify the responsible operator through your job system. Do not silently save a login page as the portal capture. Reauthenticate using the approved flow, update the state file securely, and remove obsolete state. The portal determines session lifetime and renewal rules.

sessionStorage

Playwright’s normal storage-state workflow covers cookies and local storage; its current API also documents IndexedDB state. The authentication guide describes a persistence gap for sessionStorage and shows a custom script approach to save and restore it. If the portal relies on sessionStorage, cookie and local-storage reuse alone may not restore the session. Keep any custom save/restore logic scoped to the portal’s origin and protect the exported values as credentials. See Playwright’s sessionStorage guidance.

SSO, MFA, and CAPTCHA

These controls can require a person or an approved service flow during setup or renewal. Do not attempt to defeat them. Ask the portal administrator about an officially supported API, service account, or automation procedure. The example’s interactive setup can accommodate a human completing permitted sign-in, but it does not make an interactive renewal unattended.

Consistent visual output

Fix the viewport and use a stable browser image or container if captures are compared over time. Also keep relevant rendering settings consistent. This reduces environmental variation but does not guarantee pixel-identical screenshots on every run.

7. Troubleshooting

Symptom Likely cause Fix
Capture is a login page Session expired, was revoked, or the saved state did not include the portal’s required storage. Fail the capture, renew through the approved sign-in flow, save fresh state, and verify the authenticated marker.
Setup never reaches the expected URL The post-login route differs, an interstitial appears, or sign-in requires additional approved steps. Inspect the visible browser flow and adjust the success URL condition to a real authenticated destination. Do not weaken the capture’s authentication check.
Authenticated marker times out The selector is wrong, the page is still loading, or the account lacks access to that page. Choose a stable marker unique to the signed-in view, confirm the account’s authorization, and wait for the relevant page content.
State file not found The scheduled runner uses a different working directory or the restricted file was not mounted. Use an absolute configured path or set the job’s working directory, then confirm the file is available to the job identity.
Permission denied reading state File ownership or runner permissions do not allow access. Grant read access only to the scheduled job identity and keep other users and processes excluded.
Browser fails to launch in CI Chromium or required system dependencies are missing, or the runner differs from the setup environment. Install the browser and required dependencies using the Playwright CI instructions; use a consistent supported runner/container.
Screenshot is blank or incomplete Navigation completed before application content rendered, or the target relies on delayed content. Wait for a portal-specific content marker before capture, and use a bounded timeout. Avoid relying on arbitrary long sleeps when a selector can signal readiness.
Screenshot changes between runs Browser, operating system, viewport, rendering settings, or page data changed. Pin the environment and viewport where practical, and distinguish real page changes from rendering variation.

8. Reliability, performance, and cost

A browser must start, load the portal, wait for the authenticated page, and render its content on every run. Reusing state avoids repeated password entry, but it does not eliminate browser startup, navigation time, or the need to renew an expired session. Use bounded navigation and selector timeouts, make failed authentication visible to monitoring, and avoid saving output until the page check passes.

Run captures at the frequency the use case requires, with a retention policy appropriate for customer data. Store the state file and screenshots with narrowly scoped access. Browser execution consumes runner time and resources; actual cost depends on the runner, schedule, browser environment, and retention destination. The research sources do not provide a universal runtime or cost benchmark.

Or skip the browser setup

If the page can be captured without signing into a protected account, ScreenshotNeo offers a one-call website screenshot API. Do not send a customer portal URL, credentials, cookies, or other private session material unless your access and data-handling requirements permit it. ScreenshotNeo cannot be assumed to authenticate to this unspecified portal or replace the protected Playwright workflow above.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo API documentation for request options. ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot; bot checks, blank pages, and failed loads are never billed; its MCP server lets AI agents take screenshots; and 1,000 screenshots a month are free with no card, with paid plans starting at $5 for 3,000. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month with no card.

FAQ

Does this save the portal password?

No. The setup signs in through the permitted flow and saves browser state. That state can still grant access, so protect and rotate it as you would a credential.

Can this run forever without a person?

Only if the portal’s approved authentication and renewal process supports that. Session expiry, MFA, and renewal behavior are portal-specific.

Will storageState preserve every portal login?

No. A portal may depend on sessionStorage or another mechanism, and its own authentication behavior must be checked. Playwright documents sessionStorage as requiring additional handling.

Should I use a dedicated account?

Ask the portal administrator whether a dedicated, least-privilege account is allowed. The correct account model depends on the organization’s access policy and the portal’s supported options.