Playwright Authentication Tutorial: Login Automation & Session Management

Related Courses

Next Batch : Invalid Date

Next Batch : Invalid Date

Next Batch : Invalid Date

Next Batch : Invalid Date

Playwright Authentication Tutorial: Login Automation & Session Management

Imagine a regression suite of 200 tests, and every one of them starts by opening the login page, typing a username, typing a password, and waiting for the dashboard. Each login costs a few seconds, so the suite wastes minutes per run. Then the login page changes and half the suite fails for a reason that has nothing to do with the feature under test.

Playwright Automation solves this with a simple idea: log in once, save the session, and reuse it. This Playwright authentication tutorial shows how to automate login in Playwright, how to handle different user roles, and how to manage sessions so your tests are fast and stable.

Table of Contents

  • Why Login Handling Matters in Playwright Test Automation
  • A Basic Automated Login Test
  • Reusing Sessions with storageState
  • Setting Up Authentication with a Setup Project
  • Handling Multiple User Roles
  • Faster Login Through the API
  • Playwright Session Handling: Common Pitfalls
  • Adding AI to Your Playwright Workflow
  • Best Practices Checklist
  • Frequently Asked Questions

Why Login Handling Matters in Playwright Test Automation

Almost every real application sits behind a login. If each test logs in through the UI, you pay three costs:

  • Speed: repeated logins add up across hundreds of tests.
  • Flakiness: every UI login is another chance for a timing failure.
  • Noise: a broken login page makes unrelated tests fail.

The better pattern is to test the login flow thoroughly in a few dedicated tests, then let all other tests start already authenticated.

A Basic Automated Login Test

Start with the simplest version. This is the Playwright login testing you will write first:

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

test('user can log in with valid credentials', async ({ page }) => {
  await page.goto('/login');
  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();

  await expect(page).toHaveURL(/dashboard/);
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Two habits are worth building from day one. First, use role- and label-based locators, which survive styling changes better than CSS selectors. Second, keep credentials in environment variables, never in the test file or the repository.

Also test the failure path, since invalid credentials are part of the login feature:

ts
test('shows an error for a wrong password', async ({ page }) => {
  await page.goto('/login');
  await page.getByLabel('Email').fill('[email protected]');
  await page.getByLabel('Password').fill('wrong-password');
  await page.getByRole('button', { name: 'Sign in' }).click();

  await expect(page.getByText('Invalid email or password')).toBeVisible();
});

Reusing Sessions with storageState

Playwright can save the browser’s authenticated state to a file and load it into later tests. The saved state includes cookies and local storage, so the new test starts logged in without touching the login page.

ts
// Save after logging in
await page.context().storageState({ path: 'playwright/.auth/user.json' });
ts
// Reuse in a test file
test.use({ storageState: 'playwright/.auth/user.json' });

Add the playwright/.auth folder to .gitignore. The file contains live session data and must never be committed.

Setting Up Authentication with a Setup Project

The recommended approach is a dedicated setup project that runs before your main tests. Create tests/auth.setup.ts:

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

const authFile = 'playwright/.auth/user.json';

setup('authenticate', async ({ page }) => {
  await page.goto('/login');
  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();

  await page.waitForURL(/dashboard/);
  await page.context().storageState({ path: authFile });
});
Then wire it up in playwright.config.ts:
ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },
  ],
});

The dependencies line makes Playwright run the setup first. Every test in the chromium project then starts authenticated, and the login happens once per run instead of once per test.

For tests that must start logged out, such as the login tests above, override the state:

ts
test.use({ storageState: { cookies: [], origins: [] } });

Handling Multiple User Roles

Most applications have more than one kind of user, such as an admin and a regular member. Save one state file per role:

Role

State file

Used for

Admin

playwright/.auth/admin.json

User management, settings

Member

playwright/.auth/member.json

Everyday workflows

Logged out

none

Login, signup, public pages

Create one setup step per role, then choose the file per test file or per project:

ts
test.describe('admin area', () => {
  test.use({ storageState: 'playwright/.auth/admin.json' });

  test('admin sees the user list', async ({ page }) => {
    await page.goto('/admin/users');
    await expect(page.getByRole('heading', { name: 'Users' })).toBeVisible();
  });
});

If your tests modify server-side data for the logged-in account, parallel workers can interfere with each other. In that case, give each worker its own account using a worker-scoped fixture, so tests stay independent.

Faster Login Through the API

Logging in through the UI is slow even once. If your application has a login endpoint, authenticate with Playwright’s request context and save the resulting state:

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

setup('authenticate via API', async ({ request }) => {
  const response = await request.post('/api/auth/login', {
    data: {
      email: process.env.TEST_EMAIL,
      password: process.env.TEST_PASSWORD,
    },
  });
  if (!response.ok()) throw new Error(`Login failed: ${response.status()}`);

  await request.storageState({ path: 'playwright/.auth/user.json' });
});

This works when the server sets a session cookie. If your app returns a token that the frontend stores itself, you will need to place that token into local storage or headers manually. Keep at least a few UI login tests so the real login form stays covered.

Playwright Session Handling: Common Pitfalls

These are the problems that most often break Playwright session handling:

  • Expired sessions. A saved state file can go stale. Regenerate it on every run, or in CI always run the setup project first.
  • Session storage. storageState captures cookies and local storage, but browser session storage is not saved by default. If your app depends on it, set it with an init script or restore it in a fixture.
  • Multi-factor authentication. Do not try to automate real SMS or email codes. Prefer a test environment where MFA is disabled for test accounts, or generate time-based codes from a stored secret with a TOTP library.
  • Third-party login (Google, Microsoft, and similar). These providers actively block automation. Use a test-only login route, API-created sessions, or mock the identity provider in your test environment.
  • Shared state between tests. If one test changes the account, such as changing a password, others may fail. Use dedicated accounts for destructive tests.
  • Secrets in the repository. Store credentials in CI secrets or environment variables, and keep auth files out of version control.

Adding AI to Your Playwright Workflow

AI-Powered Playwright Automation is now a practical part of many teams’ routines. Recent Playwright releases include tooling for AI assistants, such as an MCP server and test-generation agents, though the exact features change between versions, so check the current Playwright documentation before relying on them.

AI works best on the repetitive parts of authentication testing:

  • Drafting a first version of login tests from a plain-language description of the flow
  • Suggesting negative cases you may have missed, such as locked accounts, empty fields, and expired sessions
  • Helping diagnose a failing locator after a UI change

Treat the output as a draft. AI-generated tests can pass for the wrong reason, use brittle selectors, or hard-code credentials. Review every generated test, run it several times to check stability, and make sure it asserts something meaningful, not just that a page loaded.

Best Practices Checklist

  1. Test the login UI in a small, dedicated set of tests.
  2. Use a setup project and storageState for everything else.
  3. Use role- and label-based locators.
  4. Keep credentials in environment variables or CI secrets.
  5. Add auth state files to .gitignore.
  6. Use separate accounts for separate roles, and for destructive tests.
  7. Prefer API login in setup when your app supports it.
  8. Regenerate saved sessions on each CI run.
  9. Review AI-generated tests before merging them.

Frequently Asked Questions

1. How do I automate login in Playwright without repeating it in every test?

 Log in once in a setup project, save the session with storageState, and configure your test projects to load that file.

2. Where should I store the authentication state file?

 A folder such as playwright/.auth works well. Add it to .gitignore because it contains session data.

3. Can Playwright handle multi-factor authentication?

 Yes, but the practical options are disabling MFA for test accounts in a test environment or generating TOTP codes from a stored secret. Avoid automating real SMS or email delivery.

4. Why does my test lose its login halfway through?

 The usual causes are an expired session, a token stored in session storage that was not restored, or another test changing the same account. Check each of these in turn.

5. Is AI-Powered Playwright Automation reliable enough for login tests?

 It is useful for drafting tests and finding gaps, but it needs human review. Login flows are security-sensitive, so verify assertions and credential handling yourself.

Conclusion

Good Playwright test automation treats login as infrastructure, not as a step to repeat. This Playwright authentication tutorial covered the full path: test the login form properly in a few focused tests, use a setup project and storageState for everything else, and handle roles, API login, and session pitfalls deliberately. The result is a suite that runs faster and fails only when something real breaks.

For your next step, take one existing test suite this week, move its login into a setup project, and compare the total run time before and after.

If you were improving your own suite, which would you tackle first: moving to storageState for speed, or adding multiple user roles for better coverage, and why?

Follow NareshIT for more practical insights on technology, skills, and career development.