Sessions & Auth
Login & Credentials
Login is the step automation gets stuck on most often. This page lays out three reliable paths:
drive the login form directly with
snapshot → fill → click → wait, tuck accounts into the
chrome-use auth credential vault for one-command replay, and handle
Sign in with Google / OAuth plus Bitwarden passwords and passkeys.
The goal is to have the agent complete login in a real browser without leaving passwords in your shell history.
The basic login flow
The most direct approach: open the login page, use snapshot -i to read the interactive
elements in the form, pick out the @refs for the email, password, and submit button,
fill them one by one, click, and finally use wait --url to
confirm you've landed on the post-login page.
$ chrome-use open https://app.example.com/login
$ chrome-use snapshot -i
# Pick the @ref for the email / password / submit button out of the snapshot, then:
$ chrome-use fill @e3 "user@example.com"
$ chrome-use fill @e4 "hunter2"
$ chrome-use click @e5
$ chrome-use wait --url "**/dashboard"
$ chrome-use snapshot -i
@ref is a handle to an element in the current snapshot — it can go stale the moment the
page changes. Get into the habit of
running snapshot -i to grab the current refs before acting; it's far more
stable than hardcoding selectors. The final snapshot -i is there to confirm login actually
succeeded (and you didn't stall on a login page showing an error).
Credential vault (auth save / login)
For sites you log into over and over, store the credential in the local vault once and then replay it with
a single command that auto-fills and submits.
--password-stdin reads the password from standard input, keeping it out of shell history.
# Save a credential (password read from stdin — press Ctrl+D when done)
$ chrome-use auth save my-app --url https://app.example.com/login \
--username user@example.com --password-stdin
# From then on, one command: open the login page, fill, click, wait for the form to finish
$ chrome-use auth login my-app
auth save tucks credentials into chrome-use's local vault; the bwu below instead
pulls them from Bitwarden. The two work together: keep the password in Bitwarden and fetch it just-in-time
at login, so the credential never lands on the command line.
Sign in with Google / OAuth
Click the site's own Google login button, then snapshot -i: even when the GSI
"Continue as <user>" button sits inside a cross-origin iframe, you'll still get a clickable
@ref (Chrome 125+). If a account-picker popup opens instead, just switch to that popup and
drive it — the relay follows the popup your click opened.
# Kick off the OAuth flow
$ chrome-use open https://app.example.com/auth/google
# Wait for the redirect to Google, then read a fresh snapshot
$ chrome-use wait --url "**/accounts.google.com**"
$ chrome-use snapshot -i
# Fill in the Google account, step by step
$ chrome-use fill @e1 "user@gmail.com"
$ chrome-use click @e2
$ chrome-use wait 2000
$ chrome-use snapshot -i
$ chrome-use fill @e3 "password"
$ chrome-use click @e4
# Wait for the redirect back to the app site
$ chrome-use wait --url "**/app.example.com**"
Pulling passwords and passkeys from Bitwarden
When you need a real username / password (or a passkey) rather than reusing a cookie, the sister tool
bitwarden-use (bwu)
can read them out of a Bitwarden / Vaultwarden vault, letting the agent log in with credentials instead of
only going through OAuth.
bwu get <item> returns the password (and a 2FA code), and bwu fido2 get
extracts a passkey private key for sites that accept FIDO2 / WebAuthn passkeys.
# Install once
$ curl -fsSL https://raw.githubusercontent.com/leeguooooo/bitwarden-use/main/install.sh | sh
# Fetch an item's password (and 2FA), pipe it straight into the input
$ bwu get github.com | chrome-use fill '#password' --stdin
# When the site needs a FIDO2 / WebAuthn passkey, extract the passkey private key
$ bwu fido2 get
chrome-use fill '#password' --stdin reads the value to fill from standard input,
so when you pipe it in from bwu, the password never appears in command-line arguments and
never enters your history.
Handling two-factor authentication (2FA)
When you hit a second step that needs SMS / TOTP / a hardware key, the least painful approach is to let a human
do that step while the script waits alongside for the redirect. On the default extension path driving your real
Chrome, the window is already visible, so just do the second step yourself; hand control back formally with
session handoff (see Sessions & Persistence) — after handoff the
agent refuses driving commands on that session until you return it. --headed is only needed when you
--launch a separate browser, paired with a longer --timeout to wait for the post-login URL.
# Show the browser window, log in with credentials first
$ chrome-use open https://app.example.com/login --headed
$ chrome-use snapshot -i
$ chrome-use fill @e1 "user@example.com"
$ chrome-use fill @e2 "password123"
$ chrome-use click @e3
# Complete 2FA by hand in the window; the script waits for the redirect (up to 120s)
$ chrome-use wait --url "**/dashboard" --timeout 120000
HTTP Basic and cookie auth
Not every site uses a form. For HTTP Basic Auth, set the credentials before navigating; when you need to inject a session manually, you can set the cookie directly.
# HTTP Basic: set username / password before navigating
$ chrome-use set credentials username password
$ chrome-use open https://protected.example.com/api
# Set an auth cookie manually
$ chrome-use cookies set session_token "abc123xyz"
$ chrome-use open https://app.example.com/dashboard
Log in once, skip it afterward
Login is expensive — don't redo it every time. After a successful login, save the cookies and localStorage
to a state file, then load it later with --state to start up already "logged in." It's the most
practical trick for skipping the whole OAuth / 2FA dance.
# Log in once, save cookies + localStorage
$ chrome-use state save ./auth.json
# Later runs start up already logged in
$ chrome-use --state ./auth.json open https://app.example.com
--session-name (or AGENT_BROWSER_SESSION_NAME) to automatically save and load
a session by name, with no files to manage by hand. State files contain plaintext session tokens — the
details on encryption, gitignore, and named sessions are all in
Sessions & persistence.
Security notes
Login inevitably touches credentials and session tokens. A few baselines:
- Don't commit state files to a repo — they hold plaintext session tokens; add them to
.gitignoreand delete them when you're done. - Route credentials through env vars or a vault — use
auth save/bwu/--stdin/ environment variables instead of hardcoding the password in a command. - Encrypt at rest — set
AGENT_BROWSER_ENCRYPTION_KEYso saved session state is encrypted. - Clean up when done —
chrome-use cookies clearplus deleting the temporary state file; in CI, don't persist to disk at all and runclosewhen the job finishes.