> For the complete documentation index, see [llms.txt](https://learning.contextqa.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://learning.contextqa.com/testing-with-ai-coding-agents/playwright-authentication-testing.md).

# Playwright Authentication and Sessions

Build Playwright authentication tests for login, saved sessions, logout, expiry, and role isolation, then verify the same outcomes through ContextQA MCP.

## Quick answer

Test login with a fresh browser context, then verify the authenticated identity and a protected operation. Reuse saved authentication state only for tests whose subject is another feature. Keep accounts and tenant data isolated, test logout and expiry against the server's policy, and verify that denied actions leave protected data unchanged. A visible login screen or hidden button alone cannot prove access control works.

This guide concerns authentication in **your application**. Connecting a coding agent to ContextQA through MCP has its own [authentication workflow](/mcp-server/authentication.md); it does not sign the test browser into your application.

## Define the session contract first

Before asking Claude Code, Cursor, or Codex to write tests, record the approved environment, identity provider, session mechanism, disposable accounts, roles, tenant IDs, and cleanup procedure. Agree what should happen after idle expiry, absolute expiry, logout, and permission changes. Different products may intentionally have different revocation policies.

| Check                | Required evidence                                                         |
| -------------------- | ------------------------------------------------------------------------- |
| Login                | Expected identity and tenant; protected operation succeeds                |
| Saved session        | Intended account remains valid before feature assertions                  |
| Logout               | Current browser loses access; old session fails if revocation is required |
| Expiry               | Server rejects the expired session under the documented policy            |
| Wrong role or tenant | Request denied; protected state unchanged                                 |

Use an approved test tenant and identity-provider configuration. A staging fixture may provision accounts; it should not silently disable production authentication or bypass SSO/MFA. Keep a separate identity-provider integration test when the provider is part of the behavior being verified.

## Separate login coverage from authentication setup

Playwright supports saving authenticated state and loading it into isolated browser contexts. Store any files in an ignored location such as `playwright/.auth/`; they can contain reusable credentials. Shared accounts suit read-only tests, while tests changing shared server state need isolated accounts or data. See [Playwright authentication](https://playwright.dev/docs/auth).

The key testing distinction is the assertion being made. A dashboard test starting with saved state can verify dashboard behavior. It does not demonstrate that today's login form, redirect, or MFA flow works. Preserve a fresh-login test even when other tests reuse authentication.

Cookies, localStorage, and sessionStorage are not interchangeable. Playwright storage snapshots include cookies and localStorage; IndexedDB capture requires its supported option. SessionStorage needs separate handling when your application depends on it. Consult the [storage-state API](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state) for your installed version rather than copying an unfamiliar token into a different storage mechanism.

Also distinguish browser authentication from API authentication: `page.request` shares the browser context's cookie jar. It does not automatically turn a localStorage token into a bearer header. A token-based API needs its documented authentication setup. [Playwright API request context](https://playwright.dev/docs/api/class-apirequestcontext).

## Example: prove login and server-side logout

This is an **illustrative application test**, not a supplied demo application or a recorded passing run. Adapt these requirements before execution:

* Your Playwright configuration supplies an approved `baseURL`.
* `/login` has `Email`, `Password`, and `Sign in` controls; successful login reaches `/projects`.
* The application uses cookie-based sessions. `GET /api/session` returns 200 with `userId`, `role`, and `tenantId`; an invalid session returns 401 without redirecting to HTML.
* `Sign out` returns to `/login` and revokes the session on the server.
* The environment supplies a disposable viewer account through the four variables below. No other test changes this account concurrently.

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

function required(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Set ${name} for the disposable account`);
  return value;
}

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

test('login identifies the viewer and logout revokes the session',
  async ({ page, baseURL }) => {
    if (!baseURL) throw new Error('Configure an approved baseURL');
    await page.goto('/login');
    await page.getByLabel('Email', { exact: true }).fill(required('TEST_EMAIL'));
    await page.getByLabel('Password', { exact: true }).fill(required('TEST_PASSWORD'));
    await page.getByRole('button', { name: 'Sign in', exact: true }).click();
    await expect(page).toHaveURL(/\/projects\/?$/);

    const signedIn = await page.request.get('/api/session');
    expect(signedIn.status()).toBe(200);
    expect(await signedIn.json()).toMatchObject({
      userId: required('TEST_USER_ID'),
      tenantId: required('TEST_TENANT_ID'),
      role: 'viewer',
    });

    const previousState = await page.context().storageState();
    await page.getByRole('button', { name: 'Sign out', exact: true }).click();
    await expect(page).toHaveURL(/\/login\/?$/);
    expect((await page.request.get('/api/session')).status()).toBe(401);

    const previousSession = await request.newContext({
      baseURL, storageState: previousState,
    });
    try {
      expect((await previousSession.get('/api/session')).status()).toBe(401);
    } finally {
      await previousSession.dispose();
    }
  });
```

The last assertion distinguishes cookie deletion from server-side revocation. Retain it only when revocation is the agreed requirement; document a different policy explicitly. Never print the captured state. Review traces and request artifacts for credentials before sharing them.

## Add expiry and authorization cases

Use an approved short-lived test session or server-side test-clock control for expiry. Advancing only the browser's clock may change a countdown while leaving the server session valid. Deleting a browser cookie tests missing authentication, not natural server-side expiry.

For authorization, prepare separate viewer, editor, and other-tenant fixtures. Start each in a separate browser context and verify identity before the protected action. Try the forbidden operation through its documented API as well as checking the UI. Compare the protected record before and after the attempt using an authorized observer. A hidden edit button does not establish that the write endpoint rejects a viewer.

Use this bounded coding-agent prompt:

```
Read our authentication contract and existing Playwright fixtures. Add
fresh-login, logout, expiry, and wrong-role cases using disposable accounts.
Keep the expected policies fixed. Verify identity and tenant before each
action, and prove denied writes leave the target record unchanged.
Use the approved session-expiry mechanism; do not disable SSO or MFA.
Run the focused tests and preserve original failures, exact assertions,
target build, and fixture identity without recording credentials.
```

For a controlled negative check, introduce an isolated test-build defect such as omitting session revocation, then confirm the revocation assertion fails. Restore the build and rerun. A syntax error or unreachable identity provider is not the expected failure.

## Cover the managed workflow with ContextQA MCP

[Connect ContextQA MCP](/mcp-server/installation-and-setup.md), inspect its current tools, and search for matching cases before creating duplicates. Use the documented [authentication workflow patterns](/business-use-cases/complex-web-and-authentication.md) and reusable prerequisites for ordinary feature tests. Keep the actual login/logout test independent of a prerequisite that skips login.

Ask the agent to prepare separate managed cases for valid login, logout, and the approved expiry/denial scenarios. Supply the acceptance matrix and reference configured secrets rather than their values. Review generated steps for identity checks, exact denials, and unchanged state before execution. Where a server-side revocation check requires a companion API test, record that dependency instead of declaring the browser steps sufficient.

Retrieve the terminal result and available step evidence using the [execution reference](/mcp-server/tool-reference/execution-and-results.md). A failed prerequisite leaves the protected workflow unverified. A correctly denied operation is a passing negative test.

## Frequently asked questions

### Why does saved authentication work locally but fail in CI?

Check expiry, environment hostname, cookie domain, role, identity-provider policy, and fixture ownership. Recreate the session for the intended environment. Do not solve a wrong account by removing identity assertions.

### Can I reuse one administrator session for every test?

That can conceal permission defects and create shared-state interference. Use the least-privileged role required by each case and isolate mutable data. Keep role-denial tests separate from successful administrator workflows.

### Does a login redirect prove logout worked?

No. Verify the protected operation fails afterward. Where your policy requires revocation, also check the previously issued session, as the example demonstrates.

## Next step

Extend the [Playwright testing guide](/testing-with-ai-coding-agents/playwright-testing.md) with your session contract, or [book an authentication workflow demo](https://contextqa.com/book-a-demo/?utm_source=learning.contextqa.com\&utm_medium=referral\&utm_campaign=docs_demand_gen\&utm_content=playwright-authentication-testing) using one representative test environment.

Official references reviewed September 25, 2026. Recheck client APIs, application policy, and identity-provider behavior when those dependencies change.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://learning.contextqa.com/testing-with-ai-coding-agents/playwright-authentication-testing.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
