← Back to Home

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.png

The 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 dashboard

Custom 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.png

localStorage 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.png

Modern 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.png

Get 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 call

Use 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 flow

How It Works

1

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.

2

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.

3

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 TypeSolutionComplexity
Session cookiesCookie injectionEasy
Bearer token in headerHeader injectionEasy
Cognito / AmplifylocalStorage with all 3 tokensMedium
Auth0localStorage injectionMedium
Firebase AuthlocalStorage injectionMedium
OAuth popup flowManual token acquisitionHard

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 ScriptsBrowserlessRiddle
Cookie injectionYesYesYes
Header injectionYesPartialYes
localStorage injectionYes (manual)NoYes
Infrastructure neededChrome + DockerTheir serversNone (API call)
Serverless compatibleNo (250MB limit)YesYes
Full Playwright scriptsYesPuppeteer onlyYes

Pricing

Auth injection is included at no extra cost. Same pricing as regular jobs.

from $0.004
Per job

Single authenticated page

<$0.001
Per screenshot

Multiple pages in one job

Access What's Behind the Login

Create an account and start interacting with authenticated pages in minutes.