Documentation

Authentication

Test logged-in areas of your application by configuring SimuTest with one of five authentication methods — from cookie injection to automated form login.

Overview

Most applications gate their most important flows behind authentication — dashboards, checkout, account settings, admin panels. Without the ability to simulate authenticated sessions, UX testing only covers your public pages.

SimuTest supports five authentication methods. Choose the one that matches how your application manages session state:

MethodBest forSetup complexity
storage_stateExisting Playwright auth state files, complex session scenariosMedium
cookiesSession cookies exported from browser DevToolsLow
tokenJWT or API tokens stored in localStorage, sessionStorage, or a cookieLow
credentialsApplications with standard username/password login formsLow
http_basicStaging environments protected by HTTP Basic authenticationMinimal

Security note: Never hardcode credentials or tokens directly in your configuration files. Always use environment variables with the _env suffix in YAML, or read from process.env in the SDK. See Security Best Practices below.

Method 1: Storage State

Storage state is the most flexible authentication method. It mirrors Playwright'sstorageStateformat, allowing you to inject cookies and localStorage/sessionStorage values simultaneously. Use this method when you have an existing Playwright authentication setup or when your application stores session state in multiple places.

YAML configuration

Use value_env to reference an environment variable for sensitive cookie values.

tests:
  - name: "Dashboard Export (storage state)"
    url: https://app.example.com/dashboard
    task: "Export the monthly analytics report"
    auth:
      method: storage_state
      cookies:
        - name: session
          value_env: SESSION_COOKIE
          domain: app.example.com
          path: /
          httpOnly: true
          secure: true
          sameSite: Lax
      origins:
        - origin: https://app.example.com
          localStorage:
            - name: user_prefs
              value: '{"theme":"dark","locale":"en-US"}'
    criteria:
      - task_completion
      - navigation_clarity

SDK usage

import { SimuTest } from '@simutest/sdk';

const simutest = new SimuTest({ apiKey: process.env.SIMUTEST_API_KEY! });

const results = await simutest.test({
  url: 'https://app.example.com/dashboard',
  task: 'Export the monthly analytics report',
  auth: {
    method: 'storage_state',
    storageState: {
      cookies: [
        {
          name: 'session',
          value: process.env.SESSION_COOKIE!,
          domain: 'app.example.com',
          path: '/',
          httpOnly: true,
          secure: true,
          sameSite: 'Lax',
        },
      ],
      origins: [
        {
          origin: 'https://app.example.com',
          localStorage: [
            { name: 'user_prefs', value: '{"theme":"dark","locale":"en-US"}' },
          ],
        },
      ],
    },
  },
});

Exporting storage state from Playwright

If your project already uses Playwright, you can export an authenticated storage state with await page.context().storageState({ path: 'auth.json' }) after a successful login. Pass the resulting JSON directly to the storageState option.

Method 2: Cookies

Direct cookie injection is the fastest way to authenticate when your application uses session cookies. Copy the cookie values from your browser's DevTools (Application → Cookies) after logging in manually, store them as environment variables, and reference them in your config.

YAML configuration

tests:
  - name: "User Profile (cookie auth)"
    url: https://app.example.com/profile
    task: "Update the account email address"
    auth:
      method: cookies
      cookies:
        - name: auth_token
          value_env: AUTH_COOKIE_VALUE
          domain: app.example.com
          path: /
          secure: true
        - name: csrf_token
          value_env: CSRF_COOKIE_VALUE
          domain: app.example.com
          path: /
    criteria:
      - task_completion
      - error_handling

SDK usage

import { SimuTest } from '@simutest/sdk';

const simutest = new SimuTest({ apiKey: process.env.SIMUTEST_API_KEY! });

const results = await simutest.test({
  url: 'https://app.example.com/profile',
  task: 'Update the account email address',
  auth: {
    method: 'cookies',
    cookies: [
      {
        name: 'auth_token',
        value: process.env.AUTH_COOKIE_VALUE!,
        domain: 'app.example.com',
        path: '/',
        secure: true,
      },
      {
        name: 'csrf_token',
        value: process.env.CSRF_COOKIE_VALUE!,
        domain: 'app.example.com',
        path: '/',
      },
    ],
  },
});

Tip: Set an expiration date well into the future when exporting cookies for CI use, or automate cookie refresh using the credentials method instead, which handles token expiration automatically.

Method 3: Token

Token auth injects a JWT or API token into your application's client-side storage before the test session navigates to the target URL. This is the recommended method for single-page applications that read auth state from localStorage or sessionStorage on load.

The storage_type field accepts three values:

storage_typeWhere the token is injectedCommon use case
localStoragewindow.localStorage[key]SPA frameworks (React, Vue, Angular)
sessionStoragewindow.sessionStorage[key]Session-scoped auth flows
cookieHTTP cookie with the specified key nameServer-side rendered apps reading token from cookie

YAML configuration

Use token_env to reference the environment variable name that holds the token value. SimuTest reads the variable at runtime — the token is never stored in your config file.

tests:
  - name: "Admin Panel (token auth)"
    url: https://app.example.com/admin
    task: "Navigate to user management and search for a user"
    auth:
      method: token
      token_env: TEST_AUTH_TOKEN
      storage_type: localStorage
      key: authToken
    criteria:
      - task_completion
      - navigation_clarity

SDK usage

import { SimuTest } from '@simutest/sdk';

const simutest = new SimuTest({ apiKey: process.env.SIMUTEST_API_KEY! });

const results = await simutest.test({
  url: 'https://app.example.com/admin',
  task: 'Navigate to user management and search for a user',
  auth: {
    method: 'token',
    token: process.env.TEST_AUTH_TOKEN!,
    storageType: 'localStorage', // 'localStorage' | 'sessionStorage' | 'cookie'
    key: 'authToken',
  },
});

Method 4: Credentials

The credentials method automates a real login flow — SimuTest navigates to your login page, fills in the username and password fields, submits the form, and waits for the success URL before starting the test session. This is the most realistic auth method and works with any standard username/password form.

SimuTest uses sensible default selectors (input[type="email"], input[type="password"], button[type="submit"]) but you can override them with the selectors option for non-standard login forms.

YAML configuration

tests:
  - name: "Dashboard UX (credentials auth)"
    url: https://app.example.com/dashboard
    task: "Find and export the monthly analytics report"
    auth:
      method: credentials
      login_url: https://app.example.com/login
      username_env: TEST_USERNAME
      password_env: TEST_PASSWORD
      success_url: https://app.example.com/dashboard
      # Optional: custom selectors if your login form is non-standard
      selectors:
        username: 'input[name="email"]'
        password: 'input[name="password"]'
        submit: 'button[type="submit"]'
    criteria:
      - task_completion
      - navigation_clarity

SDK usage

import { SimuTest } from '@simutest/sdk';

const simutest = new SimuTest({ apiKey: process.env.SIMUTEST_API_KEY! });

const results = await simutest.test({
  url: 'https://app.example.com/dashboard',
  task: 'Find and export the monthly analytics report',
  auth: {
    method: 'credentials',
    loginUrl: 'https://app.example.com/login',
    username: process.env.TEST_USERNAME!,
    password: process.env.TEST_PASSWORD!,
    successUrl: 'https://app.example.com/dashboard',
    // Optional: override selectors for non-standard login forms
    selectors: {
      username: 'input[name="email"]',
      password: 'input[name="password"]',
      submit: 'button[type="submit"]',
    },
  },
});

Custom selectors

The selectors object accepts any valid CSS selector. Use this when your login form uses non-standard input types or when there are multiple forms on the login page. The selectors are evaluated in the context of the login_url page.

Method 5: HTTP Basic

HTTP Basic authentication is commonly used to protect staging environments and preview deployments from public access. SimuTest handles the browser authentication dialog automatically by injecting credentials at the network level.

This method sends credentials as a Base64-encoded Authorization header with every request to the target origin — matching the behavior of a browser that has already authenticated the realm.

YAML configuration

tests:
  - name: "Staging Site (HTTP basic auth)"
    url: https://staging.example.com
    task: "Verify the homepage loads correctly"
    auth:
      method: http_basic
      username_env: STAGING_USERNAME
      password_env: STAGING_PASSWORD
    criteria:
      - visual_hierarchy
      - content_clarity

SDK usage

import { SimuTest } from '@simutest/sdk';

const simutest = new SimuTest({ apiKey: process.env.SIMUTEST_API_KEY! });

const results = await simutest.test({
  url: 'https://staging.example.com',
  task: 'Verify the homepage loads correctly',
  auth: {
    method: 'http_basic',
    username: process.env.STAGING_USERNAME!,
    password: process.env.STAGING_PASSWORD!,
  },
});

Security Best Practices

Never hardcode secrets

Never put passwords, tokens, or cookie values directly in your simutest.yaml or SDK calls. Always reference environment variables using the _env suffix in YAML (e.g., token_env: MY_TOKEN) or read from process.env in the SDK.

Use dedicated test accounts

Create a dedicated test user account (e.g., simutest@yourcompany.com) with minimal permissions. Never use a production admin account. Test accounts should have read-only access where possible and should not have access to sensitive customer data.

Use st_test_ API keys for development

SimuTest issues separate API keys for development (st_test_) and production (st_live_). Use test keys in local development and CI; reserve live keys for production monitoring. Test keys are rate-limited and do not consume production session quota.

Rotate credentials regularly

Rotate test account passwords and API tokens on a regular schedule (quarterly at minimum). Update the corresponding CI/CD secrets immediately after rotation. For short-lived JWTs, generate a fresh token as part of your CI pipeline rather than storing a long-lived one.

Add simutest.yaml to .gitignore if needed

Your simutest.yaml is safe to commit as long as you use _env references for all secrets. If you ever accidentally inline a credential, revoke it immediately and rotate.

Environment Variables

SimuTest reads environment variables at runtime. Use a .env.local file for local development (never committed) and CI/CD secrets for pipeline runs.

.env.local

# .env.local — never commit this file
SIMUTEST_API_KEY=st_live_xxxxxxxxxxxxxxxxxxxxxxxx

# Test credentials (use read-only, non-production accounts)
TEST_USERNAME=simutest@yourcompany.com
TEST_PASSWORD=...

# Token auth
TEST_AUTH_TOKEN=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

# Staging basic auth
STAGING_USERNAME=preview
STAGING_PASSWORD=...

# Session cookies (export from browser DevTools)
SESSION_COOKIE=abc123...

GitHub Actions workflow (referencing repository secrets)

# .github/workflows/simutest.yml
name: SimuTest UX Audit

on:
  pull_request:
    branches: [main]

jobs:
  simutest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run SimuTest
        run: npx simutest run
        env:
          SIMUTEST_API_KEY: ${{ secrets.SIMUTEST_API_KEY }}
          TEST_USERNAME: ${{ secrets.TEST_USERNAME }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
          TEST_AUTH_TOKEN: ${{ secrets.TEST_AUTH_TOKEN }}

CI/CD secrets setup

  • GitHub Actions: Settings → Secrets and variables → Actions → New repository secret
  • GitLab CI: Settings → CI/CD → Variables → Add variable (mark as Masked)
  • CircleCI: Project Settings → Environment Variables → Add variable

Troubleshooting

credentials auth: wrong selectors

If the login flow fails, the most common cause is that SimuTest cannot locate the username or password input using the default selectors. Open your login page in Chrome DevTools and inspect the form inputs to find the correct CSS selectors, then set them explicitly using the selectors option.

Enable debug mode (SIMUTEST_DEBUG=auth) to see a screenshot of the login page at the point of failure.

CAPTCHA or 2FA blocking automated login

If your login form includes CAPTCHA or two-factor authentication, the credentials method cannot bypass it. Use storage_state or token auth instead — authenticate manually once to obtain the session state, then inject it directly.

Token or cookie has expired

If test sessions are redirected to the login page, your injected token or cookie has likely expired. For short-lived JWTs, generate a fresh token as part of your CI pipeline using your application's auth API before running SimuTest. Alternatively, switch to credentials auth, which always obtains a fresh session.

CORS errors with token injection

If your app reads the auth token via a fetch call to a different origin before navigating, localStorage injection may not work as expected because localStorage is origin-scoped. Ensure the origin in your storage state config exactly matches the origin of the page being tested (including protocol and port).