Skip to content

Automate Login Then Capture

A practical sequence for typing credentials, clicking through MFA-aware flows where possible, and capturing the post-login UI—with realistic limits for 2FA and CAPTCHA.

The goal: one API call, full browser fidelity

Teams often maintain fragile Puppeteer scripts just to log in and snap a dashboard. ScreenshotCenter collapses that into declarative automation steps executed inside managed browsers: you describe clicks, typing, waits, and navigation; the platform performs them in order, then captures the result. This post walks through a reliable pattern—automate login, then capture—and calls out where human-in-the-loop factors still apply.

Step-by-step: structure your automation

Start from the page where the user would begin authentication—often /login or a marketing home with a “Sign in” button. Chain steps roughly as follows:

  1. Navigate (implicit in the base URL) or an explicit navigate step if you must hop domains.
  2. Wait for the username field—SPAs frequently mount forms after JavaScript bundles load.
  3. Type into username and password fields; use stable selectors (name, data-testid, or scoped IDs).
  4. Click the submit control; avoid coordinates—they break under responsive layouts.
  5. Wait for a post-login selector that proves the shell loaded (navigation menu, user avatar, main grid).
  6. Optional scroll or additional clicks to reach the tab or modal you need before the shutter fires.

Feature overview and UX context for interactions live under Page interactions; the broader automation product narrative is on Automation.

Selectors that survive releases

Favor data-testid attributes or stable ARIA hooks agreed with your frontend team over long CSS chains such as div:nth-child(3) > span. When a vendor owns the login markup—hosted OAuth widgets, embedded IdP iframes—pin to selectors from their integration guide and run smoke tests after their changelog drops. Breaking selectors are the leading cause of flaky login automation; investing in test-friendly attributes upstream pays off across screenshot jobs, E2E suites, and accessibility audits.

Timing, delays, and flaky SPAs

Login screens love animation. A fixed sleep of 500 ms might work in staging and fail in production. Prefer waiting on selectors with timeouts, then add a small render delay before capture so charts finish painting. If your app kicks off XHR after mount, combine a selector wait with a post-condition such as a spinner disappearing.

Handling 2FA and CAPTCHA

Time-based OTP (TOTP) can be automated if your security policy allows a vault-backed generator—never hard-code seeds in repositories. SMS or push-based MFA generally cannot be completed inside unattended API jobs; design around them by using cookie-based sessions established through a supervised flow, or dedicated service accounts exempted from MFA per your IdP rules.

CAPTCHAs exist to block bots. Ethical and reliable automation should not attempt to bypass them. If a CAPTCHA appears after scripted login, switch to authenticated cookies captured from a legitimate session instead of driving the challenge programmatically.

Example: minimal JSON payload

{
  "url": "https://app.example.com/login",
  "delay": 1200,
  "steps": [
    { "action": "wait", "selector": "#email", "timeout": 15000 },
    { "action": "type", "selector": "#email", "text": "monitoring@example.com" },
    { "action": "type", "selector": "#password", "text": "${SECRET}" },
    { "action": "click", "selector": "button[type='submit']" },
    { "action": "wait", "selector": "[data-app='loaded']", "timeout": 60000 },
    { "action": "click", "selector": "a[href='/reports']" },
    { "action": "wait", "selector": ".report-table", "timeout": 30000 }
  ]
}

Replace ${SECRET} injection with your secret manager at runtime; do not paste real passwords into CMS or ticket systems.

Example: orchestrating from Node.js

async function captureAfterLogin() {
  const body = {
    url: "https://app.example.com/login",
    delay: 1200,
    steps: [
      { action: "wait", selector: "#email", timeout: 15000 },
      { action: "type", selector: "#email", text: process.env.MON_USER },
      { action: "type", selector: "#password", text: process.env.MON_PASS },
      { action: "click", selector: "button[type='submit']" },
      { action: "wait", selector: "[data-app='loaded']", timeout: 60000 },
    ],
  };
  const r = await fetch("https://api.screenshotcenter.com/api/screenshot/create", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-API-KEY": process.env.SCREENSHOTCENTER_API_KEY,
    },
    body: JSON.stringify(body),
  });
  if (!r.ok) throw new Error(await r.text());
  return r.json();
}

Debugging failed runs

When a step fails, reduce the chain: confirm each selector with devtools on the same viewport and locale the API uses. Export verbose job metadata if your integration supports it, and compare timestamps to see whether the wait timed out before the SPA hydrated.

Batches, storage hand-offs, and correlation

After a successful login sequence, you may capture several URLs in one batch or a short sequence of API calls. Verify whether the platform reuses the same browser context and cookie jar between shots; if not, amortize cost by front-loading authentication once and reusing session material your side controls. When results land in object storage or SaaS drives, prefix paths with tenant, environment, and date so a failed login in staging never overwrites a production artifact with the same filename pattern.

Putting it together

Declarative login automation shines for internal tools with stable markup and policies that permit unattended accounts. Where MFA or anti-bot controls intervene, combine shorter step lists with cookie forwarding. Read Automation for product-level context and Page interactions for how this capability fits alongside scrolling, element screenshots, and batch workloads.