> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stably.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Saving Authenticated State

> Skip login on every test run by saving and reusing authenticated browser state — works out of the box because Stably is 100% Playwright compatible.

Logging in before every test is slow and fragile. A better approach: **log in once, save the browser state, and reuse it across all your tests**.

Because Stably is **100% Playwright compatible**, saving authenticated state works exactly like it does in Playwright — using [`storageState`](https://playwright.dev/docs/auth). No proprietary workarounds, no vendor lock-in, no jumping through hoops like you would with other no-code testing tools.

<Tip>
  **Why this is so easy with Stably**: Unlike no-code testing platforms that require custom auth integrations or fragile cookie injection hacks, Stably uses standard Playwright under the hood. If it works in Playwright, it works in Stably — including `storageState`, setup projects, and everything else.
</Tip>

***

## How It Works

Playwright's `storageState` captures **cookies and local storage** from the browser after login. You save this to a JSON file, then tell your tests to load it — skipping the login UI entirely.

The flow is simple:

1. Run a **setup step** that logs in and saves the browser state to a file
2. All other tests **load that saved state** and start already authenticated

***

## In the Web Editor (Agent Sessions)

In Stably's Web Editor, you don't need to write any code. Just tell the AI what you want.

### Save authenticated state after login

When creating or editing a test in the Web Editor, simply prompt the AI agent:

> "Log in with the test account credentials, then save the authenticated state to `playwright/.auth/user.json`"

The AI agent will generate the appropriate Playwright `storageState` call for you.

### Reuse saved state in other tests

For subsequent tests that need to be authenticated, prompt the AI:

> "Use the saved auth state from `playwright/.auth/user.json` so I don't have to log in again"

The agent will add the correct `test.use({ storageState: ... })` configuration.

<Tip>
  This is just standard Playwright — the AI agent knows how to use `storageState` because Stably is fully Playwright compatible. No special Stably-specific auth setup required.
</Tip>

***

## In the CLI

The same approach works in the Stably CLI. Just describe what you need in your prompt.

### Interactive mode

```bash theme={null}
stably
```

Then ask:

> "Create an auth setup that logs into my app at [https://myapp.com/login](https://myapp.com/login) with the test credentials, and saves the storage state for reuse"

The agent will generate a Playwright setup project with `storageState`.

### One-shot mode

```bash theme={null}
stably create "Create an auth setup project that logs in and saves storageState to playwright/.auth/user.json, then create a test for the dashboard that reuses the saved auth"
```

***

## What the Generated Code Looks Like

For reference, here's what the AI agent generates under the hood. This is standard Playwright code — nothing proprietary.

### Auth setup (runs once)

```typescript auth.setup.ts theme={null}
import { test as setup, expect } from '@stablyai/playwright-test';

setup('authenticate', async ({ page }) => {
  // Go to login page
  await page.goto('https://myapp.com/login');

  // Fill in credentials
  await page.getByLabel('Email').fill(process.env.TEST_EMAIL!);
  await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!);
  await page.getByRole('button', { name: 'Sign in' }).click();

  // Wait for login to complete
  await expect(page).toHaveURL('/dashboard');

  // Save the authenticated state
  await page.context().storageState({ path: 'playwright/.auth/user.json' });
});
```

### Tests that reuse the state

```typescript dashboard.spec.ts theme={null}
import { test, expect } from '@stablyai/playwright-test';

// Load the saved auth state — no login needed
test.use({ storageState: 'playwright/.auth/user.json' });

test('view dashboard', async ({ page }) => {
  await page.goto('https://myapp.com/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
```

### Playwright config with setup project

```typescript playwright.config.ts theme={null}
import { defineConfig } from '@stablyai/playwright-test';

export default defineConfig({
  projects: [
    // Setup project runs first and saves auth state
    { name: 'setup', testMatch: /.*\.setup\.ts/ },

    // All tests depend on setup and use the saved state
    {
      name: 'tests',
      dependencies: ['setup'],
      use: {
        storageState: 'playwright/.auth/user.json',
      },
    },
  ],
});
```

***

## Multiple Accounts

Need different roles (admin, regular user, viewer)? Save separate state files:

```typescript theme={null}
// admin.setup.ts
await page.context().storageState({ path: 'playwright/.auth/admin.json' });

// user.setup.ts
await page.context().storageState({ path: 'playwright/.auth/user.json' });
```

Then use the right state per test:

```typescript theme={null}
// Admin tests
test.describe('admin panel', () => {
  test.use({ storageState: 'playwright/.auth/admin.json' });

  test('manage users', async ({ page }) => {
    // Already logged in as admin
  });
});

// Regular user tests
test.describe('user dashboard', () => {
  test.use({ storageState: 'playwright/.auth/user.json' });

  test('view profile', async ({ page }) => {
    // Already logged in as regular user
  });
});
```

In the Web Editor or CLI, just tell the agent:

> "Set up auth for two roles: an admin and a regular user. Save each to separate state files and create tests that use the correct role."

***

## Tips

<AccordionGroup>
  <Accordion title="Add playwright/.auth to .gitignore">
    The saved state files contain session tokens. Don't commit them:

    ```text .gitignore theme={null}
    playwright/.auth/
    ```
  </Accordion>

  <Accordion title="Store credentials securely">
    Use [Stably Environments](/stably2/environments) to store test credentials as secret variables rather than hardcoding them. In the CLI, pass them via environment variables.
  </Accordion>

  <Accordion title="Handle token expiry">
    If your auth tokens expire quickly, the setup project runs fresh on each `npx playwright test` invocation — so state is always current. For long-lived CI runs, consider adding a TTL check or forcing a fresh login.
  </Accordion>

  <Accordion title="Combine with Google SSO">
    If your app uses Google login, combine `storageState` with Stably's [`authWithGoogle`](/stably2/auth/auth-with-google) helper to save authenticated Google sessions.
  </Accordion>
</AccordionGroup>

***

## Why This Is Easier Than Other Tools

|                    | Stably                                    | Typical no-code tools                              |
| ------------------ | ----------------------------------------- | -------------------------------------------------- |
| **Auth mechanism** | Standard Playwright `storageState`        | Proprietary cookie injection or replay             |
| **Setup**          | Tell the AI agent in plain English        | Configure through vendor-specific UI               |
| **Multiple roles** | Separate state files, standard Playwright | Often requires per-role configuration in dashboard |
| **Portability**    | Pure Playwright — works anywhere          | Locked to the vendor's runtime                     |
| **Debugging**      | Read the generated `.json` state file     | Black box                                          |

Because Stably is built on Playwright, you get the full power of Playwright's auth handling with none of the vendor lock-in. Your auth setup is just code — portable, version-controlled, and debuggable.
