Auth and Browser Storage

Back

Loading concept...

Playwright Auth & Browser Storage: Your Secret Vault Guide

The Magic Vault Analogy

Imagine you have a magical vault in your house. Every time a friend comes over, instead of asking them to prove who they are (show ID, answer questions), you simply hand them a golden key that says “I know this person!”

That’s exactly what Playwright authentication and browser storage does for your tests. Instead of logging in again and again (boring!), you save the “golden key” once and reuse it everywhere.


What is Storage State?

Think of storage state as a snapshot of everything the browser remembers:

  • Cookies (like little notes the website leaves)
  • Local storage (your browser’s memory drawer)
  • Session storage (temporary sticky notes)

Simple Example:

// Save everything the browser knows
await context.storageState({
  path: 'auth.json'
});

When you save storage state, you’re basically telling Playwright:

“Take a photo of all the browser’s memories RIGHT NOW!”


Saving Auth State

Why Save Auth State?

Every time you run a test, logging in takes time:

  1. Type username
  2. Type password
  3. Click login
  4. Wait for redirect
  5. Wait for dashboard…

That’s 5-10 seconds wasted. Every. Single. Test.

How to Save It

// After successful login, save the state
const context = await browser.newContext();
const page = await context.newPage();

// Do the login once
await page.goto('https://myapp.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'secret123');
await page.click('button[type="submit"]');

// Wait until fully logged in
await page.waitForURL('**/dashboard');

// NOW save the magic!
await context.storageState({
  path: './auth-state.json'
});

The auth-state.json file now contains your “golden key”!


Reusing Authentication

Now comes the fun part. You have the key. Let’s use it!

// Create a new context WITH your saved state
const context = await browser.newContext({
  storageState: './auth-state.json'
});

const page = await context.newPage();

// Go directly to protected page - no login needed!
await page.goto('https://myapp.com/dashboard');

// You're already logged in!

What Just Happened?

graph TD A["Test Starts"] --> B["Load auth-state.json"] B --> C["Browser Already Has Cookies"] C --> D["Visit Dashboard Directly"] D --> E["No Login Required!"] style A fill:#e8f5e9 style E fill:#c8e6c9

It’s like your browser woke up already remembering who you are!


Global Setup for Auth

The Problem

What if you have 100 tests? Do you want to:

  • Save auth state in each test? ❌
  • Log in before each test? ❌

The Solution: Global Setup

Create a special file that runs once before all tests:

// global-setup.ts
import { chromium } from '@playwright/test';

async function globalSetup() {
  const browser = await chromium.launch();
  const context = await browser.newContext();
  const page = await context.newPage();

  // Login once for everyone
  await page.goto('https://myapp.com/login');
  await page.fill('#username', 'testuser');
  await page.fill('#password', 'secret123');
  await page.click('button[type="submit"]');
  await page.waitForURL('**/dashboard');

  // Save for all tests to share
  await context.storageState({
    path: './playwright/.auth/user.json'
  });

  await browser.close();
}

export default globalSetup;

Then in your config:

// playwright.config.ts
export default {
  globalSetup: require.resolve('./global-setup'),
  use: {
    storageState: './playwright/.auth/user.json'
  }
};

Now ALL your tests start logged in!


Per-Worker Authentication

What’s a Worker?

Playwright can run tests in parallel using multiple “workers” (like having multiple helpers).

The Problem

If all workers use the same account, they might step on each other’s toes!

The Solution: Give Each Worker Its Own Account

// auth.setup.ts
import { test as setup } from '@playwright/test';

const authFile =
  `./playwright/.auth/user-${process.env.TEST_WORKER_INDEX}.json`;

setup('authenticate', async ({ page }) => {
  // Each worker gets unique credentials
  const workerIndex = process.env.TEST_WORKER_INDEX || '0';

  await page.goto('/login');
  await page.fill('#username', `user${workerIndex}`);
  await page.fill('#password', 'password123');
  await page.click('button[type="submit"]');

  await page.waitForURL('**/dashboard');
  await page.context().storageState({ path: authFile });
});
graph TD A["Worker 0"] --> B["user0 auth"] C["Worker 1"] --> D["user1 auth"] E["Worker 2"] --> F["user2 auth"] B --> G["Tests for Worker 0"] D --> H["Tests for Worker 1"] F --> I["Tests for Worker 2"] style A fill:#bbdefb style C fill:#c8e6c9 style E fill:#fff9c4

Each worker has its own account. No fighting!


Cookie Operations

Cookies are like little sticky notes websites leave in your browser.

Reading Cookies

// Get all cookies
const cookies = await context.cookies();

console.log(cookies);
// [{name: 'session', value: 'abc123', ...}]

Reading Specific Cookie

// Get cookies for a specific URL
const cookies = await context.cookies(
  'https://myapp.com'
);

// Find the one you need
const sessionCookie = cookies.find(
  c => c.name === 'session_id'
);

console.log(sessionCookie?.value);

Adding Cookies

// Add a cookie manually
await context.addCookies([{
  name: 'my_cookie',
  value: 'cookie_value',
  domain: 'myapp.com',
  path: '/'
}]);

Clearing Cookies

// Clear all cookies
await context.clearCookies();

// Now you're logged out!

Common Cookie Properties:

Property What It Means
name Cookie’s name
value What it stores
domain Which site owns it
path Which pages can see it
httpOnly Can JavaScript read it?
secure HTTPS only?

Local Storage Access

Local Storage is like a drawer in your desk that stays there forever (until you clean it).

Reading Local Storage

// First, go to the page
await page.goto('https://myapp.com');

// Read a specific item
const token = await page.evaluate(() => {
  return localStorage.getItem('authToken');
});

console.log(token); // "abc123xyz"

Writing to Local Storage

// Set an item directly
await page.evaluate(() => {
  localStorage.setItem('theme', 'dark');
  localStorage.setItem('language', 'en');
});

Reading All Items

// Get everything in local storage
const allData = await page.evaluate(() => {
  const data = {};
  for (let i = 0; i < localStorage.length; i++) {
    const key = localStorage.key(i);
    data[key] = localStorage.getItem(key);
  }
  return data;
});

console.log(allData);
// {theme: 'dark', authToken: 'abc123', ...}

Clearing Local Storage

await page.evaluate(() => {
  localStorage.clear();
});

Session Storage Access

Session Storage is like temporary sticky notes - they disappear when you close the tab!

The Key Difference

Feature Local Storage Session Storage
Lifetime Forever Until tab closes
Shared across tabs? Yes No
Size limit ~5-10MB ~5-10MB

Reading Session Storage

// Read specific item
const tempData = await page.evaluate(() => {
  return sessionStorage.getItem('currentStep');
});

Writing to Session Storage

await page.evaluate(() => {
  sessionStorage.setItem('formData',
    JSON.stringify({name: 'John', step: 2})
  );
});

Reading All Session Items

const sessionData = await page.evaluate(() => {
  const data = {};
  for (let i = 0; i < sessionStorage.length; i++) {
    const key = sessionStorage.key(i);
    data[key] = sessionStorage.getItem(key);
  }
  return data;
});

Clearing Session Storage

await page.evaluate(() => {
  sessionStorage.clear();
});

Putting It All Together

Here’s a complete real-world example:

import { test, expect } from '@playwright/test';

// This test uses pre-saved auth state
test('dashboard shows user data', async ({ page }) => {
  // Already logged in from storageState!
  await page.goto('/dashboard');

  // Check cookies
  const cookies = await page.context().cookies();
  expect(cookies.find(c => c.name === 'session'))
    .toBeTruthy();

  // Check local storage
  const token = await page.evaluate(() =>
    localStorage.getItem('authToken')
  );
  expect(token).toBeTruthy();

  // Do your actual test
  await expect(page.locator('h1'))
    .toContainText('Welcome');
});

Quick Reference

graph LR A["Storage State"] --> B["Cookies"] A --> C["Local Storage"] A --> D["Session Storage"] B --> E["context.cookies&#35;40;&#35;41;"] B --> F["context.addCookies&#35;40;&#35;41;"] B --> G["context.clearCookies&#35;40;&#35;41;"] C --> H["localStorage.getItem&#35;40;&#35;41;"] C --> I["localStorage.setItem&#35;40;&#35;41;"] D --> J["sessionStorage.getItem&#35;40;&#35;41;"] D --> K["sessionStorage.setItem&#35;40;&#35;41;"] style A fill:#e3f2fd style B fill:#fff3e0 style C fill:#e8f5e9 style D fill:#fce4ec

Your Learning Journey Complete!

You’ve learned how to:

  • Save your browser’s “memory” with storage state
  • Skip login in every test by reusing authentication
  • Set up global auth that runs once for all tests
  • Give each parallel worker its own account
  • Read, write, and clear cookies
  • Access local storage data
  • Work with session storage for temporary data

You’re now a Playwright Auth Master!

Remember: The goal is simple - log in once, test forever!

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.