master
John Lauer Update 5 file(s) 552edca 3mo ago

Gusto (gusto.com) — payroll

  • Login entry URL: https://app.gusto.com/login (redirects to login.gusto.com, a Keycloak IdP)
  • Flow type: email-first → account-picker → password → 2FA
  • 2FA: TOTP authenticator (6-digit code); "Remember device for 30 days" checkbox offered — check it on adom-you so future logins skip 2FA
  • Anti-automation notes: the password field is a React-controlled masked input and is type="text" (not type=password), with autocomplete="current-password". Setting .value does NOT register with React's state — you MUST use real keystrokes (pup_type) and then read the value back to confirm it landed before submitting.

Steps

  1. Open on adom-you (never isolated) — pup_open_window {url:"https://app.gusto.com/login"}. AUTO-APPROVES. If adom-you already has a Gusto session you land signed in; done.
  2. Email step — fill the email field (benign, AI may enter it) and click Continue. pup_type or a fill; AUTO-APPROVES (username is not a secret).
  3. Account picker — Gusto often shows "Select your email address" with your account as a row. Click the row for your email (a <button>). AUTO-APPROVES.
  4. Password step ("Enter your password") — the field is #password, type=text, masked by React. Use pup_login {username, password, submit:true, submitReason:"…confirm login.gusto.com is the intended site…"} (structured verb → passes the safety check) OR pup_type {selector:"#password", text, clear:true} then verify #password value length, then click Continue. This is the step that PAUSES for the human under Auto mode (typing a credential) — surface it for one-tap approval, or the human types it. Real keystrokes are mandatory; .value-set silently fails.
  5. 2FA — "Enter the 6-digit code in your authenticator app." pup CANNOT generate this; the human enters the 6-digit code from their authenticator (PAUSES). Check "Remember device" so it is a one-time step on adom-you.
  6. Redirects to app.gusto.com/… dashboard = signed in.

Field selectors

  • username/email: input[type=email], input[name=username] (type: email)
  • password: #password — NOTE: type="text", autocomplete="current-password", not type=password
  • submit: button with text "Continue"

Gotchas

  • type=password selectors miss the password field entirely (it is type=text). pup ≥2.0.123 also matches autocomplete=current-password / name=password.
  • Setting .value on the password field passes React's display but submits EMPTY ("Password is required"). Use real keystrokes and verify the value landed.
  • On the email step, the value can reset on reload — re-enter and submit via the form, then poll for the password field.
  • It is a cross-origin SSO redirect (login.gusto.com → app.gusto.com); capture on password blur (pup ≥2.0.121) so the credential is vaulted before the redirect.