Many websites require authentication before allowing access to protected content or actions. Browserbase provides flexible methods for handling authentication in automated sessions, ensuring seamless logins while maintaining security and efficiency.
Why Authentication Matters in Automation
- Ensures access to restricted content without manual intervention.
- Reduces session expiration issues by persisting login states.
- Prevents bot detection and account lockouts with stealth techniques.
Strategies for Handling Authentication
Handling authentication in automation requires maintaining session state, avoiding bot detection, and resolving challenges like CAPTCHAs or multi-factor authentication (MFA). Browserbase provides several strategies to help you authenticate reliably while ensuring security and efficiency.
- Create a session with context, proxies, and fingerprinting.
- Use the Session Live View to log into the website.
- Use the context ID to persist the authentication state across future sessions.
Create an Session with Contexts, Proxies, and Fingerprinting
Ensure seamless authentication by persisting login sessions, avoiding detection, and preventing IP-based blocking.
- Apply Contexts → Store cookies, session tokens, and local storage to prevent repeated logins. Log in once, then reuse the saved authentication state.
- Enable Stealth Mode → Adjust browser, OS, and screen settings to blend in with real users and bypass detection.
- Use Proxies → Rotate residential proxies and match IP locations to prevent tracking and login restrictions.
By combining contexts, stealth mode, and proxies, you can create secure, stable, and automated authentication workflows.
import { Browserbase } from "@browserbasehq/sdk";
async function createAuthSession(contextId: string) {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!,
browserSettings: {
context: {
id: contextId,
persist: true
}
},
proxies: [{
type: "browserbase",
geolocation: {
city: "New York",
state: "NY",
country: "US"
}
}]
});
console.log("Session URL: https://browserbase.com/sessions/" + session.id);
return session;
}
// Use the context ID from your saved context
const contextId = "<context-id>";
const session = await createAuthSession(contextId);
console.log("Session URL: https://browserbase.com/sessions/" + session.id);
import { Browserbase } from "@browserbasehq/sdk";
async function createAuthSession(contextId: string) {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!,
browserSettings: {
context: {
id: contextId,
persist: true
}
},
proxies: [{
type: "browserbase",
geolocation: {
city: "New York",
state: "NY",
country: "US"
}
}]
});
console.log("Session URL: https://browserbase.com/sessions/" + session.id);
return session;
}
// Use the context ID from your saved context
const contextId = "<context-id>";
const session = await createAuthSession(contextId);
console.log("Session URL: https://browserbase.com/sessions/" + session.id);
from browserbase import Browserbase
import os
bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
def create_auth_session(context_id: str):
session = bb.sessions.create(
project_id=os.environ["BROWSERBASE_PROJECT_ID"],
browser_settings={
"context": {
"id": context_id,
"persist": True
}
},
proxies=[{
"type": "browserbase",
"geolocation": {
"city": "New York",
"state": "NY",
"country": "US"
}
}]
)
print("Session URL: https://browserbase.com/sessions/" + session.id)
return session
# Use the context ID from your saved context
context_id = "<context-id>"
session = create_auth_session(context_id)
Use the Session Live View to login
For authentication workflows, the best practice is to log in manually once using Session Live View, then persist the authentication state across future sessions using contexts. This approach ensures secure, repeatable logins without needing manual input every time.
- Start a new session and retrieve the Session Live View URL.
- Open the Live View in your browser to interact with the session in real time.
- Once logged in, the session’s authentication data (cookies, session tokens) is stored.
- Save the session context id so future sessions can reuse the authentication state without logging in again.
Taking a Session's Remote Control with Session Live View
Incorporate a human in the loop to complete the authentication process.
Use the context ID to persist the authentication state across future sessions
After logging in once, you can reuse the authentication state by storing it in a context. This allows future sessions to bypass the login process, maintaining access to authenticated pages without needing manual input.
Now, any session using this context.id will start already logged in, eliminating the need to authenticate again. By persisting authentication with contexts, you can ensure seamless automation, reduce login failures, and improve session continuity.
2FA Challenges
Two-step verification (via authenticator apps or SMS) or magic links usually require human intervention in the loop. There are 2 main strategies to manage 2FA:
- Disable 2FA or create an app password
- Enable Remote Control of your Session
Disable 2FA or create an app password
For an internal tool, try to turn off the
two-step verification.
For an authentication flow requiring some level of security,
try to create an app password.
Enable Remote Control of your Session
If a two-step verification mechanism cannot be bypassed or disabled, consider handing back control to the end user by leveraging the Session Live URLs.
Taking a Session's Remote Control with Session Live View
Let your end users complete the two-step verification process as part of your
automation.
Accessing an authentication flow with Stealth Mode
Many authentication flows implement mechanisms to prevent web automation:
- IP addresses restrictions
- User agent filtering
- Captchas
- Rate limiting
When running your browser session, dealing with these impediments may require setting up IP
rotations with proxies along with captcha solving and fingerprint generators.
By automating with Browserbase, you get opt-in proxies, automatic, fully configurable
fingerprinting, and captcha solving—without any coding:
Speed up your automation by reusing cookies
Some websites or web apps rely on cookies-based Sessions, which can be easily retrieved and reused to speed up your automation.
The code examples below showcases how to retrieve and set cookies to avoid your automation to go through the authentication flow at each run:
import Browserbase from "@browserbasehq/sdk";
import { chromium } from "playwright-core";
import storage from "./storage.js";
async function authenticate(page, context) {
const session = await storage.getSession();
if (session) {
await context.addCookies([session]);
// try to access a protected page
await page.goto("https://www.browserbase.com/overview");
if (page.url === "https://www.browserbase.com/overview") {
// no redirect -> we are authenticated, let's skip the authentication flow
return;
}
}
await page.goto("https://www.browserbase.com/sign-in");
// ... sign-in ...
// retrieve User Session Cookie
const cookies = await context.cookies();
const sessionCookie = cookies.find((c) => c.name === "session_id");
await storage.storeSession(sessionCookie);
}
(async () => {
const bb = new Browserbase({
apiKey: process.env.BROWSERBASE_API_KEY
});
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
proxies: true
});
const browser = await chromium.connectOverCDP(session.connectUrl);
// Getting the default context to ensure the sessions are recorded.
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
await authenticate(page, defaultContext);
// ... interact with page ...
await page.close();
await browser.close();
})().catch((error) => console.error(error.message));
import Browserbase from "@browserbasehq/sdk";
import { chromium } from "playwright-core";
import storage from "./storage.js";
async function authenticate(page, context) {
const session = await storage.getSession();
if (session) {
await context.addCookies([session]);
// try to access a protected page
await page.goto("https://www.browserbase.com/overview");
if (page.url === "https://www.browserbase.com/overview") {
// no redirect -> we are authenticated, let's skip the authentication flow
return;
}
}
await page.goto("https://www.browserbase.com/sign-in");
// ... sign-in ...
// retrieve User Session Cookie
const cookies = await context.cookies();
const sessionCookie = cookies.find((c) => c.name === "session_id");
await storage.storeSession(sessionCookie);
}
(async () => {
const bb = new Browserbase({
apiKey: process.env.BROWSERBASE_API_KEY
});
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID,
proxies: true
});
const browser = await chromium.connectOverCDP(session.connectUrl);
// Getting the default context to ensure the sessions are recorded.
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
await authenticate(page, defaultContext);
// ... interact with page ...
await page.close();
await browser.close();
})().catch((error) => console.error(error.message));
# The first time this file is run, the authentication cookies will be stored
# to a file. Subsequent runs will load those cookies from the file.
import json
import os
from browserbase import Browserbase
from playwright.sync_api import sync_playwright, Page
API_KEY = os.environ["BROWSERBASE_API_KEY"]
SITE_URL = "https://practice.expandtesting.com"
SITE_LOGIN_URL = f"{SITE_URL}/login"
SITE_PROTECTED_URL = f"{SITE_URL}/secure"
# Credentials to use for testing the login flow.
# For testing only! Don't store secrets in code.
SITE_USERNAME = "practice"
SITE_PASSWORD = "SuperSecretPassword!"
# This would typically be stored in some other durable storage or even kept in
# memory. Here, we're just going to serialize them to disk using json dump/load.
# Ensure these are well secured as anyone with this information can log in!
COOKIE_FILE = "test-cookies.json"
def store_cookies(browser_tab: Page):
# Retrieve all the cookies for this URL
all_cookies = browser_tab.context.cookies(SITE_URL)
# You might want to put these in some durable storage, but for now
# just keep them in a simple file as JSON.
with open(COOKIE_FILE, "w") as cookie_file:
json.dump(all_cookies, cookie_file, indent=4)
print(f"Saved {len(all_cookies)} cookie(s) from the browser context")
def restore_cookies(browser_tab: Page):
# Return all cookies to the browser context
try:
with open(COOKIE_FILE) as cookie_file:
cookies = json.load(cookie_file)
except FileNotFoundError:
# No cookies to restore
return
browser_tab.context.add_cookies(cookies)
print(f"Restored {len(cookies)} cookie(s) to the browser context")
def authenticate(browser_tab: Page):
# Navigate to the sign-in page, enter the site credentials and sign in
print("Attempting to log in")
browser_tab.goto(SITE_LOGIN_URL)
browser_tab.get_by_role("textbox", name="username").fill(SITE_USERNAME)
browser_tab.get_by_role("textbox", name="password").fill(SITE_PASSWORD)
browser_tab.get_by_role("button", name="Login").click()
# Store the site cookies
store_cookies(browser_tab)
def run(browser_tab: Page):
# Load up any stored cookies
restore_cookies(browser_tab)
# Instruct the browser to go to a protected page
browser_tab.goto(SITE_PROTECTED_URL)
if browser_tab.url != SITE_PROTECTED_URL:
# Redirected, almost certainly need to log in.
authenticate(browser_tab)
# Try again
browser_tab.goto(SITE_PROTECTED_URL)
# Print out a bit of info about the page it landed on
print(f"{browser_tab.url=} | {browser_tab.title()=}")
...
with sync_playwright() as playwright:
bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
# A session is created on the fly
session = bb.sessions.create(
project_id=os.environ["BROWSERBASE_PROJECT_ID"],
proxies=True
)
browser = playwright.chromium.connect_over_cdp(session.connectUrl)
# Print a bit of info about the browser we've connected to
print(
"Connected to Browserbase.",
f"{browser.browser_type.name} version {browser.version}",
)
context = browser.contexts[0]
browser_tab = context.pages[0]
try:
# Perform our browser commands
run(browser_tab)
finally:
# Clean up
browser_tab.close()
browser.close()
Handling Passkeys
Passkeys are a modern authentication method that can present challenges for automation since they typically require user interaction. When automating sites that use passkeys, you’ll often want to disable or bypass them since the required user interactions aren’t supported in automated sessions.
Disable Passkeys in Your Session
To prevent passkey prompts from appearing and potentially blocking your automation, you can disable them using the Chrome DevTools Protocol (CDP). Here’s how to do it:
import { chromium } from "playwright-core";
import { Browserbase } from "@browserbasehq/sdk";
async function createSessionWithoutPasskeys() {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!
});
console.log(`Session created, id: ${session.id}`);
// Connect to the remote browser
const browser = await chromium.connectOverCDP(session.connectUrl);
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
// Create a CDP session and configure the virtual authenticator
const client = await page.context().newCDPSession(page);
await client.send('WebAuthn.enable');
await client.send('WebAuthn.addVirtualAuthenticator', {
options: {
protocol: 'ctap2',
transport: 'internal',
hasResidentKey: true,
hasUserVerification: true,
isUserVerified: true,
automaticPresenceSimulation: true,
},
});
return page;
}
import { chromium } from "playwright-core";
import { Browserbase } from "@browserbasehq/sdk";
async function createSessionWithoutPasskeys() {
const bb = new Browserbase({ apiKey: process.env.BROWSERBASE_API_KEY! });
const session = await bb.sessions.create({
projectId: process.env.BROWSERBASE_PROJECT_ID!
});
console.log(`Session created, id: ${session.id}`);
// Connect to the remote browser
const browser = await chromium.connectOverCDP(session.connectUrl);
const defaultContext = browser.contexts()[0];
const page = defaultContext.pages()[0];
// Create a CDP session and configure the virtual authenticator
const client = await page.context().newCDPSession(page);
await client.send('WebAuthn.enable');
await client.send('WebAuthn.addVirtualAuthenticator', {
options: {
protocol: 'ctap2',
transport: 'internal',
hasResidentKey: true,
hasUserVerification: true,
isUserVerified: true,
automaticPresenceSimulation: true,
},
});
return page;
}
from browserbase import Browserbase
from playwright.sync_api import sync_playwright
import os
def create_session_without_passkeys():
bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])
session = bb.sessions.create(
project_id=os.environ["BROWSERBASE_PROJECT_ID"]
)
print(f"Session created, id: {session.id}")
# Connect to the remote browser
with sync_playwright() as playwright:
browser = playwright.chromium.connect_over_cdp(session.connect_url)
context = browser.contexts[0]
page = context.pages[0]
# Create a CDP session and configure the virtual authenticator
client = page.context.new_cdp_session(page)
client.send("WebAuthn.enable")
client.send("WebAuthn.addVirtualAuthenticator", {
"options": {
"protocol": "ctap2",
"transport": "internal",
"hasResidentKey": True,
"hasUserVerification": True,
"isUserVerified": True,
"automaticPresenceSimulation": True,
}
})
return page
This code:
- Creates a new Browserbase session
- Connects to the browser using CDP
- Enables the WebAuthn API
- Adds a virtual authenticator that prevents real passkey prompts
By setting up this virtual authenticator, you prevent the browser from prompting for actual passkey authentication, allowing your automation to proceed with other authentication methods like username/password.
Alternative Authentication Methods
When passkeys are enabled on a site, there’s usually an alternative authentication method available (like username/password). After disabling passkeys, look for these alternative methods:
- “Sign in with password” links
- “Other sign-in options” buttons
- Username/password form toggles