See and Use Authenticated Pages
Dashboards, admin panels, staging environments—anything behind a login. Inject cookies, localStorage, or headers. Navigate, click, fill forms, and screenshot without re-authenticating.
Key insight: Modern SPAs store auth in localStorage, not just cookies. Inject tokens the same way the auth library would.
Quick Start
Cookie injection in 5 lines:
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://app.example.com/dashboard", "options": {"cookies": [{"name": "session", "value": "abc123", "domain": "app.example.com"}]}}' \
-o dashboard.pngThe Authentication Problem
Most browser automation requires re-authenticating for every session. When you need to interact with authenticated content:
Re-login Every Time
Standard browser automation restarts fresh. Each session requires a full login flow— 30+ seconds wasted, credentials exposed, rate limits triggered.
Session Management Hell
Maintaining browser sessions across requests means managing browser pools, cookie stores, and dealing with session expiration. Complex infrastructure for simple automation.
CI/CD Complexity
Testing authenticated flows in pipelines requires storing credentials, handling 2FA, and managing test accounts. Most teams just skip these tests.
AI Agent Loops
Vision-based AI agents need screenshots of authenticated pages. Re-logging in for each observation burns tokens and time. The login loop kills agent performance.
Inject Auth, Skip the Login
Pass your auth credentials with the request. We inject them before navigating. Interact with authenticated content as if you're already logged in.
Cookie Injection
For session-based auth (most web apps):
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://app.example.com/dashboard",
"options": {
"cookies": [
{
"name": "session_id",
"value": "abc123xyz",
"domain": "app.example.com",
"path": "/",
"httpOnly": true,
"secure": true
}
]
}
}' -o dashboard.png
# Result: Screenshot of authenticated dashboardCustom Headers
For Bearer tokens, API keys, or custom auth schemes:
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://api.example.com/admin",
"options": {
"headers": {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIs...",
"X-API-Key": "your-internal-api-key"
}
}
}' -o admin.pnglocalStorage Injection
For SPAs that store tokens in localStorage:
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://spa.example.com/settings",
"options": {
"localStorage": {
"auth_token": "eyJhbGciOiJIUzI1NiIs...",
"user_id": "12345"
}
}
}' -o settings.pngModern Auth Providers (Cognito, Auth0, Firebase)
Modern auth libraries like AWS Cognito, Auth0, and Firebase store tokens in localStorage with specific key formats. Simple form-fill login attempts often fail because these libraries use JavaScript-heavy flows with multiple tokens.
The key insight: you need ALL tokens with the EXACT key format the library expects.
AWS Cognito / Amplify
Cognito requires the idToken, accessToken, refreshToken, LastAuthUser, and clockDrift:
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-amplify-app.com/dashboard",
"options": {
"localStorage": {
"CognitoIdentityServiceProvider.YOUR_CLIENT_ID.user@example.com.idToken": "eyJ...",
"CognitoIdentityServiceProvider.YOUR_CLIENT_ID.user@example.com.accessToken": "eyJ...",
"CognitoIdentityServiceProvider.YOUR_CLIENT_ID.user@example.com.refreshToken": "eyJ...",
"CognitoIdentityServiceProvider.YOUR_CLIENT_ID.LastAuthUser": "user@example.com",
"CognitoIdentityServiceProvider.YOUR_CLIENT_ID.user@example.com.clockDrift": "0"
}
}
}' -o dashboard.pngGet your tokens by calling Cognito's InitiateAuth API directly, or copy them from your browser's DevTools → Application → Local Storage after logging in manually.
Auth0
Auth0 uses a similar pattern:
{
"options": {
"localStorage": {
"auth0.YOUR_CLIENT_ID.user@example.com.idToken": "eyJ...",
"auth0.YOUR_CLIENT_ID.user@example.com.accessToken": "eyJ..."
}
}
}Firebase Auth
Firebase typically uses:
{
"options": {
"localStorage": {
"firebase:authUser:YOUR_API_KEY:[DEFAULT]": "{\"uid\":\"...\",...}"
}
}
}Check your app's localStorage in DevTools to find the exact key format your auth library uses.
Full Python Example (Cognito)
End-to-end: get tokens from Cognito, then use them with Riddle:
import requests
# 1. Get tokens from Cognito
auth_response = requests.post(
"https://cognito-idp.us-east-1.amazonaws.com/",
headers={
"Content-Type": "application/x-amz-json-1.1",
"X-Amz-Target": "AWSCognitoIdentityProviderService.InitiateAuth"
},
json={
"AuthFlow": "USER_PASSWORD_AUTH",
"ClientId": "YOUR_COGNITO_CLIENT_ID",
"AuthParameters": {
"USERNAME": "user@example.com",
"PASSWORD": "your-password"
}
}
)
tokens = auth_response.json()["AuthenticationResult"]
# 2. Use tokens with Riddle
client_id = "YOUR_COGNITO_CLIENT_ID"
username = "user@example.com"
response = requests.post(
"https://api.riddledc.com/v1/run",
headers={"Authorization": "Bearer YOUR_RIDDLE_API_KEY"},
json={
"url": "https://your-app.com/dashboard",
"options": {
"localStorage": {
f"CognitoIdentityServiceProvider.{client_id}.{username}.idToken": tokens["IdToken"],
f"CognitoIdentityServiceProvider.{client_id}.{username}.accessToken": tokens["AccessToken"],
f"CognitoIdentityServiceProvider.{client_id}.{username}.refreshToken": tokens["RefreshToken"],
f"CognitoIdentityServiceProvider.{client_id}.LastAuthUser": username,
f"CognitoIdentityServiceProvider.{client_id}.{username}.clockDrift": "0"
}
}
}
)
# Screenshot returned directly (sync mode)
with open("dashboard.png", "wb") as f:
f.write(response.content)With Structured Steps API
Multi-step authenticated workflows with /v1/run:
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"steps": [
{ "goto": "https://app.example.com/dashboard" },
{ "screenshot": "dashboard" },
{ "click": "[data-tab=analytics]" },
{ "waitFor": ".analytics-loaded" },
{ "screenshot": "analytics" },
{ "click": "[data-tab=settings]" },
{ "screenshot": "settings" }
],
"options": {
"cookies": [
{ "name": "session", "value": "abc123", "domain": "app.example.com" }
]
}
}'
# Result: 3 screenshots of authenticated pages, one API callUse Cases
Visual Regression Testing
Compare dashboard states across deploys. Inject test user credentials, screenshot after each deploy, diff the PNGs. Catch visual bugs in authenticated flows.
AI Agent Vision
Your agent logs in once, extracts the session cookie, then passes it to every Riddle call. No login loop, no wasted tokens, no re-authentication delays.
Customer Support Tools
Screenshot what the customer sees. Inject their session (with permission) and capture their exact view for debugging. No "can you send a screenshot?" back-and-forth.
Staging & Preview Deploys
Screenshot Vercel preview URLs, staging environments, or internal tools that require auth. Pass the auth headers and get the screenshot.
Complex Workflows Behind Auth
Run complete Playwright scripts with pre-injected auth. Multi-page flows, form interactions, data extraction, screenshots—all without login overhead.
curl -X POST "https://api.riddledc.com/v1/run" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"script": "
// Auth is already injected - go straight to protected content
await page.goto('https://app.example.com/dashboard');
await saveScreenshot('dashboard');
// Navigate to settings
await page.click('[data-testid=settings-link]');
await page.waitForURL('**/settings');
await saveScreenshot('settings');
// Export some data
await page.click('button:has-text("Export")');
await page.waitForSelector('.export-complete');
await saveScreenshot('export-done');
",
"options": {
"cookies": [
{"name": "session", "value": "xyz789", "domain": "app.example.com"}
]
}
}'
# Result: 3 screenshots of authenticated multi-page flowHow It Works
You Provide Auth
Include cookies, headers, or localStorage in your request. We accept any auth mechanism your target site uses—session cookies, JWTs, API keys, custom headers.
We Inject Before Navigation
Before the browser navigates to your URL, we inject all provided auth. Cookies are set, headers are configured, localStorage is populated. The page loads as if you're already logged in.
Within a single script request, injected auth remains active across all page navigations and interactions.
Execute & Clean Up
We run your workflow—clicks, form fills, screenshots, data extraction—then dispose the browser. Your credentials never persist beyond the request.
Common Pitfalls
Approaches that seem like they should work, but often don't with modern SPAs:
Form Fill + Click Submit
Filling in email/password fields and clicking submit often fails with Cognito, Auth0, or OAuth flows. These use JavaScript-heavy authentication that doesn't complete properly with simple form automation.
Injecting Only the ID Token
Many auth libraries require multiple tokens (idToken, accessToken, refreshToken) to consider the user authenticated. Setting just one token leaves the app in an invalid auth state.
Wrong localStorage Key Format
Cognito keys must be exactly CognitoIdentityServiceProvider.{clientId}.{username}.idToken. A typo or wrong format means the SDK won't find the tokens.
Setting Tokens After Navigation
If you navigate to the dashboard first, then inject tokens, the app already checked auth on load and redirected you. Tokens must be injected BEFORE navigating to authenticated pages.
Redeclaring the Page Variable
In script mode, page is already provided by Riddle. Writing const page = ... causes "Identifier already declared" errors. Just use page directly.
Debugging Tips
When auth injection isn't working, here's how to diagnose:
Check console.json in Artifacts
Every job captures console output in console.json. Look for auth errors, failed token validation, or redirect loops. This is often where you'll find the real error.
Take Intermediate Screenshots
Screenshot before and after auth injection to see exactly what state the page is in.
Log localStorage Keys
Add console.log(Object.keys(localStorage)) to see what keys exist and compare to what the app expects.
Check network.har
The HAR file shows all network requests. Look for failed auth calls or unexpected redirects.
Quick Reference
| Auth Type | Solution | Complexity |
|---|---|---|
| Session cookies | Cookie injection | Easy |
| Bearer token in header | Header injection | Easy |
| Cognito / Amplify | localStorage with all 3 tokens | Medium |
| Auth0 | localStorage injection | Medium |
| Firebase Auth | localStorage injection | Medium |
| OAuth popup flow | Manual token acquisition | Hard |
Key insight: Modern SPAs store auth state in localStorage, not cookies. Inject tokens the same way the auth library would.
Security Considerations
Ephemeral Sessions
Each request gets a fresh browser. Auth is injected, screenshot taken, browser destroyed. Credentials don't persist between requests or leak to other users.
TLS Everywhere
All API calls use HTTPS. Your auth credentials are encrypted in transit. We never log cookie values or header contents.
Your Responsibility
You control what credentials you send. Use service accounts, test users, or scoped tokens. Don't send production admin credentials—use the minimum permissions needed.
Short-Lived Tokens
For maximum security, use short-lived tokens. Generate a token, use it for screenshots, let it expire. Works great with OAuth refresh flows.
vs. Alternatives
| Puppeteer Scripts | Browserless | Riddle | |
|---|---|---|---|
| Cookie injection | Yes | Yes | Yes |
| Header injection | Yes | Partial | Yes |
| localStorage injection | Yes (manual) | No | Yes |
| Infrastructure needed | Chrome + Docker | Their servers | None (API call) |
| Serverless compatible | No (250MB limit) | Yes | Yes |
| Full Playwright scripts | Yes | Puppeteer only | Yes |
Pricing
Auth injection is included at no extra cost. Same pricing as regular jobs.
Single authenticated page
Multiple pages in one job
Access What's Behind the Login
Create an account and start interacting with authenticated pages in minutes.