How do AI agents log into authenticated websites?

Agent login decomposes into three orthogonal problems: which credential to type (vault), how to handle the second factor (digital identity), and how much authenticated state to carry between runs (browser profile). Combine the right primitive for each, wrap a verifier around the result, and the login runs autonomously.
How do AI agents log into authenticated websites?
Login isn't one problem. It's three orthogonal problems: which credential to type, what to do when the second factor appears, and how much authenticated state to carry between runs. Most agent demos ignore at least two of those — which is why most demos shatter the first time the site changes a flow or the cookie expires. The right way to think about agent login is to pick a primitive for each sub-problem rather than try to solve them all in the prompt.
The three sub-problems
- Credential injection. Where does the username and password come from, and how do they reach the form without entering the LLM's prompt or tool-call payload? Answer: a credential vault. The agent emits a placeholder, the vault swaps in the real value at the action layer.
- Second factor. When 2FA hits, where does the code come from? Answer: a digital identity's SMS-capable phone, programmatically-readable email inbox, or vault-stored TOTP. See how AI agents handle 2FA.
- Persistence. Should the session start fresh every time, or already authenticated? Answer: a browser profile. Bootstrap with
persist=Trueonce; reuse withpersist=Falsethereafter.
Combine the right answers and the login runs autonomously. Skip any of them and the run blocks.
Three login patterns
Most workflows fall into one of three shapes. Pick the lightest-weight option that fits.
Pattern A — Predictable login (vault + agent)
Agent navigates, fills credentials from the vault, clicks submit. Best for sites with a standard email+password form and no 2FA, or with 2FA the digital identity can solve.
from notte_sdk import NotteClient
client = NotteClient()
vault = client.Vault(vault_id="my_vault")
vault.add_credentials(
url="https://service.com",
email="agent@example.com",
password="...",
)
with client.Session() as session:
agent = client.Agent(vault=vault, session=session, max_steps=10)
agent.run(task="Log into service.com and download my last invoice.")Pattern B — Sign-up or recurring 2FA (digital identity + vault)
Agent uses an identity to enroll a new account or to sign in to a service that re-challenges with SMS / email codes. The identity provides the readable channels; the attached vault holds the credentials.
from notte_sdk import NotteClient
client = NotteClient()
identity = client.Persona(create_vault=True)
identity.add_credentials(url="https://service.com")
with client.Session() as session:
agent = client.Agent(persona=identity, session=session, max_steps=15)
agent.run(
task="Sign up at service.com and complete the email + SMS verification.",
)Pattern C — SSO, sticky-device, or custom flow (browser profile)
A human (or the developer, once) completes the login through Notte's session viewer with persist=True. Subsequent agent runs attach the same profile and start authenticated.
from notte_sdk import NotteClient
client = NotteClient()
profile = client.profiles.create(name="acme-sso")
# One-time bootstrap — often performed manually through the session viewer.
with client.Session(
profile={"id": profile.profile_id, "persist": True},
) as session:
session.execute(type="goto", url="https://acme.com/sso")
# ... complete the login by hand or programmatically
# Every later run starts authenticated. persist=False is read-only and
# safe for parallel use.
with client.Session(
profile={"id": profile.profile_id, "persist": False},
) as session:
agent = client.Agent(session=session, max_steps=10)
agent.run(task="Pull this quarter's reports from the dashboard.")Verifying the login actually worked
A login that "succeeds" but lands the agent on a 2FA challenge isn't a successful login. Production agents wrap a verifier around the login step — a deterministic check (target URL, presence of a known logged-in element) or an LLM judge — that confirms the post-login state was reached. Without one, downstream actions silently run against a logged-out page.
Common pitfalls
- Cookie-only persistence without a profile. Hand-managing storage state between runs leaks. Timestamps drift, fingerprints change, the second login looks like a fresh device. Use a profile.
- Trusting the LLM to "remember the password from last turn." It can't, and shouldn't. Always inject through the vault.
- Treating 2FA as one-time setup. Many sites re-challenge periodically; the identity's channels need to keep working over weeks, not just at first run.
- Skipping the verifier. Easy to miss in a demo, expensive to debug in production.
Key takeaways
- Agent login decomposes into three orthogonal sub-problems: credential, second factor, persistence.
- Map each to a primitive — vault, digital identity, browser profile — then wrap a verifier on top.
- Three patterns cover almost every shape: vault-only for predictable flows, identity for sign-ups and recurring 2FA, profile for SSO and sticky-device flows.
- When the agent gets blocked on any path, the universal escape hatch is a profile bootstrapped manually.