Home > Blog > Playwright: Streamlining Authentication with storageState
Author: Tomotaka ASAGI
Published: Jan 24, 2026
Introduction
In the previous three articles of this series, I covered Selenium vs Playwright, Java vs TypeScript, and Cucumber.js vs Playwright-bdd.
Starting from this article, I'll introduce features that impact test design and test case creation when using Playwright. These features are unique to Playwright and not found in traditional tools like Selenium or JUnit.
This time, I'll explain how to streamline authentication using storageState and Projects.
The "Precondition" Problem with Login
In automated testing, login itself is rarely the actual test target. More often, login is a "precondition" for running the tests.
In functional testing and regression testing, most tests look like "verify screen X while logged in." This means if you have 100 tests, you're logging in 100 times.
What does this mean?
If login is unstable, your entire test suite becomes unstable.
Have you ever received a flood of test failure notifications, only to find that every single failure was due to login issues? The test results you actually cared about get buried under login problems. This is a huge waste.
There are various workarounds for this problem, but what I find most compelling about storageState is that Playwright can solve this on its own as an E2E browser testing framework.
storageState: Saving and Restoring Authentication State
The Problem with Traditional Approaches
When writing tests with Selenium, I used to include login processing in every test that required authentication.
Test 1: Login → Screen A → Verify
Test 2: Login → Screen B → Verify
Test 3: Login → Screen C → Verify
Issues:
- Login processing runs every time → Test execution takes longer
- If login process changes, all tests need modification
- Higher load on the login page
- If login is unstable, the entire test suite becomes unstable
What is storageState?
Playwright's storageState is a feature that saves browser authentication state (Cookies, LocalStorage) to a JSON file and restores it in subsequent tests.
// Save authentication state
await page.context().storageState({ path: '.auth/user.json' });
The saved JSON file contains:
- Cookies (session tokens, etc.)
- LocalStorage (JWT tokens, etc.)
Important Note
The information saved in storageState contains sensitive data such as session tokens.
Make sure to add it to .gitignore.
# .gitignore
playwright/.auth/
Methods for Using storageState
There are several methods for using storageState.
Method | Overview |
Projects + storageState | Authenticate in setup project, load in test project |
globalSetup + storageState | Authenticate in globalSetup, load via global configuration |
test.use() per file | Specify JSON file to load for each test file |
Comparison of Methods
Aspect | Projects (Recommended) | globalSetup | test.use() |
Displayed in HTML Report | ✅ Yes | ❌ No | — (no setup) |
Trace recording | ✅ Automatic | ❌ Requires separate setup | — (no setup) |
Fixtures available | ✅ Yes | ❌ No | ✅ Yes |
Ease of debugging | ◎ | △ | ○ |
Pre-creation of auth state | Automatic (created in setup) | Automatic (created in globalSetup) | Manual pre-creation required |
Switching between multiple roles | ◎ (per project) | △ (requires workarounds) | ◎ (per file) |
The official documentation recommends using Projects.
For this use case (verifying items across multiple screens after authentication), the Projects approach is most suitable.
Reasons:
- Authentication setup is displayed in HTML Report with trace recording
- Easier to debug when authentication fails
- Fixtures can be used
Projects: Defining Test Dependencies
What are Projects?
Playwright's Projects is a feature that allows you to group tests and define dependencies. This enables you to declaratively write flows like "run authentication setup first, then run tests."
Basic Configuration
Key points:
dependencies: ['setup']ensures the setup project runs firststorageStateensures tests start in an authenticated state- Setup appears in HTML Report and traces are recorded
What Happens When Setup Fails
An important characteristic of Projects is that if Setup fails, dependent tests are not run—they are skipped.
The official documentation clearly states:
"If the tests from a dependency fails then the tests that rely on this project will not be run."
Scenario | Traditional (login every time) | Projects + storageState |
Login fails | All 100 tests fail | Setup fails once, dependent tests skipped |
Failure notification | "They're all login failures!" | Clearly shows "Setup failed" |
Wasted execution | Attempts login 100 times | Login runs only once |
This means:
- The cause of failure is clear
- No wasted test execution
- Less noise in test results, so you can focus on real issues
Authentication Setup Example
Test File Example
// dashboard.spec.ts
import { test, expect } from '@playwright/test';
test('Dashboard - Display verification', async ({ page }) => {
// Already authenticated due to storageState
// No login process needed!
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
Why You Don't Need to Write Authentication Code in Tests
You might wonder why tests are in an authenticated state even though there's no explicit code to load authentication information in the test code.
The answer lies in the playwright.config.ts configuration:
{
name: 'chromium',
use: {
storageState: 'playwright/.auth/user.json', // ← This is the key
},
dependencies: ['setup'],
}
This storageState setting ensures that:
- All tests belonging to this project automatically load this JSON file
- The browser context is initialized with the saved Cookies/LocalStorage
- Tests receive a
pagethat is already authenticated
In other words, the two settings serve different roles:
Setting | Role |
dependencies: ['setup'] | Ensures setup project runs first (creates JSON file) |
storageState: '...' | Automatically loads saved auth state (makes tests authenticated) |
This mechanism keeps test code simple and eliminates the need to worry about authentication processing.
Using with Playwright-bdd
In the previous article, I introduced Playwright-bdd. Even when using storageState, I recommend explicitly writing the authentication precondition in Feature files.
Feature: Customer Management
Background:
Given the user is logged in
Scenario: Display customer details
When opening the customer details screen
Then customer information is displayed correctly
However, the implementation of this Given step changes.
Implementation using storageState:
Given('the user is logged in', async ({ page }) => {
// Already authenticated due to storageState
// No actual login process needed
// Just verify authentication state if necessary
await page.goto('/');
await expect(page.getByText('Welcome')).toBeVisible();
});
Key points:
- Explicitly state the precondition "user is logged in" in the Feature file
- Actual login processing is replaced by storageState
- Step definition implementation can be simplified to just verifying authentication state
Good to know:
If storageState and Projects dependencies are correctly configured, tests will run in an authenticated state even without writing the "user is logged in" step in the Feature file. However, I recommend writing it explicitly to improve Feature file readability and clarify test preconditions.
Summary
Feature | Traditional Problem | Playwright Solution |
storageState | Login process every time | Save and restore authentication state |
Projects | Setup management is cumbersome | Declaratively define dependencies |
These features are only available in Playwright Test (TypeScript version). They are concrete examples of "things you can't do with the Java version" mentioned in Part 2.
In the next article, I'll introduce efficient verification methods using Soft Assertions.
References
- Playwright Authentication
- Playwright Projects
This article is Part 4 of the "Playwright Series."
- Part 1: From Selenium to Playwright
- Part 2: TypeScript vs Java - Feature Differences by Language
- Part 3: BDD Framework Comparison - Cucumber.js vs Playwright-bdd
- Part 4: Streamlining Authentication with storageState (this article)
- Part 5: Efficient Verification with Soft Assertions (next)