Skip to main content
Auth flows are one of the hardest parts of E2E testing. Google sign-in screens can change, 2FA challenges are time-sensitive, and bot protections can make UI automation flaky. This guide is auth-only: how to test Google Auth and 2FA reliably in Playwright. Use a layered approach:
  1. Run one dedicated auth smoke test that proves Google Auth + 2FA still works.
  2. Save storageState after login and reuse it for the rest of your suite.
  3. Keep shared auth accounts stable and isolated from personal accounts.
This gives you coverage of the real login path without making every test depend on a fragile identity flow. If you use the Stably SDK, prefer context.authWithGoogle() over driving Google’s UI directly. It is purpose-built for automated runs and supports OTP via otpSecret.
auth/google-auth.spec.ts
import { test, expect } from '@stablyai/playwright-test';

test('authenticate with Google + 2FA', async ({ context, page }) => {
  await context.authWithGoogle({
    email: process.env.E2E_GOOGLE_EMAIL!,
    password: process.env.E2E_GOOGLE_PASSWORD!,
    otpSecret: process.env.E2E_GOOGLE_OTP_SECRET!,
  });

  await page.goto('/dashboard');
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
See full options and setup details in Auth with Google.

Fallback: UI Auth Setup Project

Create a setup project that logs in once and stores session state.
auth/setup-google-auth.ts
import { test as setup, expect } from '@playwright/test';

setup('authenticate with Google + 2FA', async ({ page }) => {
  await page.goto(process.env.BASE_URL!);
  await page.getByRole('button', { name: 'Continue with Google' }).click();

  // Use dedicated test credentials only.
  await page.getByLabel('Email or phone').fill(process.env.E2E_GOOGLE_EMAIL!);
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByLabel('Enter your password').fill(process.env.E2E_GOOGLE_PASSWORD!);
  await page.getByRole('button', { name: 'Next' }).click();

  // Your app-specific post-login assertion.
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
  await page.context().storageState({ path: 'playwright/.auth/google-user.json' });
});
Then consume the stored state in all authenticated tests:
test.use({ storageState: 'playwright/.auth/google-user.json' });

Handling 2FA Reliably

Pick one method and standardize it:
If your IdP supports conditional policies, allow a test account to bypass the second factor in non-production environments. Keep one real 2FA smoke test in CI and run all other tests with bypass.
Store the test account’s TOTP seed in a secret manager and generate the current code in setup. This is stable and does not require inbox/SMS polling.
Poll your test inbox or SMS provider API for the OTP, then submit it in Playwright. This matches production behavior but is slower and typically more flaky.

Example: TOTP in Setup

auth/setup-google-auth-with-totp.ts
import { test as setup } from '@playwright/test';
import { authenticator } from 'otplib';

setup('google auth with totp', async ({ page }) => {
  await page.goto(process.env.BASE_URL!);
  await page.getByRole('button', { name: 'Continue with Google' }).click();

  await page.getByLabel('Email or phone').fill(process.env.E2E_GOOGLE_EMAIL!);
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByLabel('Enter your password').fill(process.env.E2E_GOOGLE_PASSWORD!);
  await page.getByRole('button', { name: 'Next' }).click();

  const code = authenticator.generate(process.env.E2E_TOTP_SECRET!);
  await page.getByLabel('Enter code').fill(code);
  await page.getByRole('button', { name: 'Verify' }).click();

  await page.context().storageState({ path: 'playwright/.auth/google-user.json' });
});

CI/CD Auth Checklist

  • Use dedicated Google workspace test users.
  • Store auth variables in Stably Environments and run with npx stably test --env=Staging.
  • Store credentials and TOTP seed as secrets.
  • Rotate shared test credentials on a schedule.
  • Fail fast if auth setup fails instead of retrying all tests.
  • Keep auth smoke tests small and isolated.

Stably Features to Use for Auth Stability

Do not automate personal Google accounts or rely on one-time manual approvals in CI. Use dedicated test identities only.