How login, logout, session timeout, and two-factor authentication get tested automatically, without test users and secrets turning into the actual risk
The login process is the entry point to virtually every personalized feature of a Magento application, from order history through saved addresses to the wish list, and at the same time one of the most frequently, silently broken areas of a test suite, since login tests are especially prone to orphaned test users, expired test sessions, and accidentally checked-in credentials. A cleanly built authentication test flow treats login, logout, session timeout, and two-factor authentication as separate, clearly distinguished test cases backed by thoughtful test user management, instead of blending every aspect into a single, cluttered test case.
Table of Contents
- 1. Why authentication deserves its own test area
- 2. Login and logout as separate test cases
- 3. Deliberately testing session timeout without real waiting
- 4. Automatically testing two-factor authentication
- 5. Managing test users across a team without collisions
- 6. Handling tokens in tests without secrets in the repository
- 7. Reliably testing the password reset flow
- 8. Parallel logins and multi-device sessions
- 9. Authentication test cases at a glance
- 10. Summary
- 11. FAQ
1. Why authentication deserves its own test area
Authentication differs from most other test areas in that it cuts across virtually every other feature of an application: a broken login flow doesn't just block the login test itself, but potentially every downstream test that assumes a logged-in state too, say tests for order history, the customer account, or saved payment methods, turning a single login flow bug into a cascade of failing tests across the entire test report.
For this reason, it pays to deliberately treat authentication tests as a separate test area running early in the suite, whose outcome immediately indicates whether the basic foundation for all subsequent, login-dependent tests is even sound, instead of only noticing a failed login late, somewhere in the middle of a long test run, after dozens of downstream tests have already failed for the same underlying reason.
2. Login and logout as separate test cases
A solid login test checks not just the successful path with correct credentials, but deliberately also the expected error states: a wrong combination of email address and password, a locked account after too many failed attempts, and the correct behavior for a not-yet-confirmed email account, if the application requires such confirmation.
The logout test, in turn, should check not just that the visible user state correctly switches to logged out, but also that a subsequent, direct navigation to an actually protected page, say order history, reliably redirects to the login page, instead of wrongly still showing protected content, which would indicate an incompletely invalidated session.
test('login with correct credentials leads to the customer account', async ({ page }) => {
await page.goto('/customer/account/login');
await page.fill('#email', testUser.email);
await page.fill('#pass', testUser.password);
await page.click('#send2');
await expect(page).toHaveURL(/customer\/account/);
});
test('logout fully invalidates the session', async ({ page }) => {
await loginAs(page, testUser);
await page.click('[data-testid="customer-logout"]');
await page.goto('/sales/order/history');
await expect(page).toHaveURL(/customer\/account\/login/);
});
3. Deliberately testing session timeout without real waiting
Actually testing a session timeout by really waiting out the configured expiry time is barely viable in any CI pipeline, since session lifetimes in Magento typically span several hours and a test correspondingly can't block for hours, which calls for a different approach to reliably and quickly check the same behavior.
One practical way is to deliberately reduce the session configuration in the test environment to a very short value of a few seconds, so real expiry actually happens within a reasonable test window instead of being artificially simulated. Alternatively, the session cookie can be directly manipulated or removed in the test to produce the same expired state without actually waiting, though this second variant, while faster, is less realistic, since it doesn't run through the real server-side expiry mechanism but only reproduces its symptom.
4. Automatically testing two-factor authentication
Two-factor authentication presents E2E tests with a particular hurdle, since the second factor typically gets delivered over an external channel like SMS or an authenticator app, which an automated test naturally has no direct access to, making a direct reproduction of the real user flow barely possible without extra effort.
In practice, it has proven effective to store a deterministic TOTP secret key in the test user's profile for testing purposes and compute the currently valid one-time code in the test itself, using the same TOTP library real authenticator apps rely on, instead of waiting for a real SMS or push notification. This approach realistically tests the application's actual verification mechanism, without depending on an external, uncontrollable communication channel.
import { authenticator } from 'otplib';
test('login with 2FA accepts a valid TOTP code', async ({ page }) => {
await loginAs(page, twoFactorTestUser);
const code = authenticator.generate(twoFactorTestUser.totpSecret);
await page.fill('[data-testid="totp-code"]', code);
await page.click('[data-testid="totp-submit"]');
await expect(page).toHaveURL(/customer\/account/);
});
5. Managing test users across a team without collisions
A common mistake in growing test suites is using a single, permanently shared test user for all login tests across the whole team, causing unpredictable collisions the moment two parallel test runs use the same user at once, say when one test run is currently changing this user's password while a second, parallel test run tries to log in with the old password.
More robust is programmatically creating a fresh, unique test user at the start of every single test run, say directly through the Magento REST API instead of the slower UI, combined with a reliable cleanup routine removing that test user again once the test finishes. This approach not only fully avoids collisions between parallel test runs, but also keeps the test database clean long term, instead of letting hundreds of orphaned test accounts accumulate in the production or staging database over months.
6. Handling tokens in tests without secrets in the repository
For API-driven test setup, say programmatically creating a test user via the REST API before the actual UI test, an admin or integration token is needed that must under no circumstances sit directly in the test code or in a versioned configuration file, since such a token, once accidentally checked in, stays permanently in the Git history even if it later gets removed from the current file state.
The correct place for such credentials is environment variables set exclusively at runtime by the CI pipeline or the local development environment, typically via the given CI platform's secret management, combined with a clear .gitignore rule for all local .env files, so a token never accidentally ends up in the repository through a regular commit.
Likewise, the test user's password itself should never appear as a fixed, recurring value in the source code, but instead get generated randomly at runtime and used exclusively within that test run, so that even an accidentally publicly visible test report never exposes genuinely reusable credentials that could get abused against another system or service.
7. Reliably testing the password reset flow
Despite its apparent simplicity, the password reset flow is one of the most frequently overlooked test areas, even though it gets used regularly in real operation and, if broken, can permanently lock customers out of their own account. A complete test covers both sending the reset email to a known email address and the deliberately non-revealing behavior for an unknown address, since an application shouldn't reveal, for security reasons, whether a given email address actually exists as a customer account.
For automatically checking the actual reset link, usually delivered by email, a locally run mail catcher like Mailhog or Mailpit is a good fit, intercepting outgoing test emails and making them programmatically readable through its own API, instead of depending on a real, publicly reachable mailbox. The test extracts the reset token contained in the email directly from this intercepted message, then navigates to the corresponding reset page and checks that a new password can be successfully set and that the previously used token can't be used again afterward.
8. Parallel logins and multi-device sessions
Shoppers today are frequently logged in on several devices at once, say in a desktop browser plus a mobile view on a smartphone, which is why a realistic test case should check that a login on a second device doesn't automatically invalidate the session on the first device, provided the application is meant to deliberately support several concurrent sessions.
For applications that, for security reasons, deliberately allow only a single active session per user account, the opposite holds: a test should in this case deliberately check that a new login on a second device actually reliably ends the previous session, instead of both sessions silently staying valid in parallel, which would pose a security risk if the first device had meanwhile been lost or stolen.
9. Authentication test cases at a glance
The table below summarizes the presented test cases around authentication.
| Test case | Goal | Note |
|---|---|---|
| Login with correct credentials | Successful base flow | Foundation for all downstream tests |
| Login with wrong credentials | Checking error handling | Include lockout behavior after failed attempts |
| Logout | Full session invalidation | Check access to a protected page afterward |
| Session timeout | Checking expiry behavior | Short test configuration instead of real waiting |
| 2FA login | Second factor correctly verified | Compute the TOTP code deterministically in the test |
Mironsoft
E2E test strategy, CI integration, and stable test suites
Test suites that actually find bugs instead of just blinking red?
We review existing E2E test suites for flakiness, missing test isolation, and inefficient CI runtimes, then build a test strategy that genuinely creates confidence instead of just checking a box.
Test Audit
Systematically uncovering flaky tests, testing pyramid gaps, and coverage blind spots.
CI Optimization
Building parallel execution, retry strategies, and fast feedback loops.
Cypress/Playwright Setup
Setting up robust E2E suites for Magento frontends from the ground up.
10. Summary
Auth Flow Tests: The Essentials at a Glance
Core idea
Treat login, logout, session timeout, and 2FA as separate, clearly distinguished test cases.
Test users
Fresh, unique test user per test run instead of a shared, collision-prone account.
2FA approach
Compute the TOTP code deterministically in the test instead of waiting for external SMS or push.
Secrets
Tokens only through environment variables, never in versioned test code.