Authentication Testing

Authentication testing checks that a system verifies who a user is and resists the ordinary ways of getting in as someone else: guessing passwords, abusing the forgot-password flow, skipping the second factor, or using the application’s own messages to learn which accounts exist. It covers five areas: password policy, lockout and throttling, multi-factor authentication (MFA), reset flows and user enumeration.

A concrete example: a shop’s login answers "No account with this e-mail" for unknown addresses and "Wrong password" for known ones. Both messages are functionally correct, but to an authentication tester the pair is one defect: an attacker can now check a list of a million addresses against the shop and attack only the real customers.

Why it matters

Authentication is the front door. The OWASP Top 10 gives authentication failures a category of their own (you met it in the lesson on the OWASP Top 10), and many real break-ins need no clever exploit: a password leaked from another site, a reset link that never expires, a login that never slows down. The cost is account takeover, with the fraud, data loss and regulatory damage that follow.

For the tester it is a productive area: most checks have a clear expected result that a published guideline spells out, NIST SP 800-63B for passwords and attempt limits and the OWASP cheat sheets for reset flows and enumeration, and almost all of it needs only a browser, an HTTP client and two test accounts. What it does need is permission: test only systems you own or have written authorisation to test, with accounts created for testing.

One sign-in attempt, and where each check lives

A typical login handler runs the same steps in the same order, and each step is where one class of test lives: look the account up, count the attempt against a limit, compare the password with the stored hash (a one-way fingerprint of the password, so the server never needs to keep the password itself), ask for a second factor if the account has one, and only then create a session. Password policy is enforced earlier, at sign-up and password change, and the reset flow is a side door into the same account.

At every step ask two questions: is the check done at all, and does it leak anything — a different message, status code or response time — that tells an attacker more than "no"?

A sign-in attempt in order: account lookup, attempt counter, password check, second factor, then a session; each exit holds one class of authentication test, sign-up enforces the policy and reset is a side door.
  1. 1Account lookupFind the account by e-mail or usernameTest: enumeration

    Branch: Unknown account: the same generic error, status and timing as a wrong password

  2. 2Attempt counterCount failures per account; slow down or lock after the limitTest: lockout and throttling

    Branch: Over the limit: a delay, a CAPTCHA or a temporary lock, with a message that reveals nothing

  3. 3Password checkCompare with the stored salted hash, never with plain textTest: storage and truncation

    Branch: Wrong password: generic error, counter +1

  4. 4Second factorA one-time code or security key, accepted once, with its own attempt limitTest: MFA

    Branch: Wrong or reused code: rejected; the step cannot be skipped

  5. 5Session issuedOnly after every check has passed; the next lesson tests the session itself
Outside the sequence
  • Sign-up and password changeWhere the policy is enforced: at least 15 characters (8 with MFA), 64 or more allowed, Unicode and spaces, a blocklist, no composition rules
  • Forgot passwordA side door to the same account: a random, single-use, short-lived token; after the change, old sessions end or the user is offered the option to end them

Illustrative order: the exact steps differ by product.

Password policy: test what NIST SP 800-63B asks for

NIST SP 800-63B, the US federal guideline for authentication that many organisations outside government also adopt, reverses several old habits. Its current edition, revision 4 (2025), requires at least 15 characters when the password is the only factor and allows a minimum of 8 when the password is used only together with a second factor; the 2017 edition, which many products were built on, set 8 for both. The verifier (the system that checks the password) should allow at least 64 characters and accept all printing ASCII characters, the space and Unicode; it must check the whole password, never a truncated part, and must compare every new password with a blocklist of commonly used, expected or compromised values. Revision 4 forbids composition rules ("one uppercase, one digit, one symbol") and forced periodic changes without evidence of compromise, which the 2017 edition already advised against, and both editions forbid password hints and security questions. Password managers and autofill must work, paste should be allowed, and the user should be able to display the password while typing.

That is your test oracle, once the team has said which edition and which case the product follows. Apply boundary value analysis to the length, counting each Unicode code point as one character, as SP 800-63B specifies: 14, 15, 64 and 65 for a login protected by the password alone, or 7 and 8 instead of 14 and 15 when a second factor is always required. Try an Arabic or Devanagari password with spaces, and submit values the blocklist should contain: a common password padded to the minimum, such as Password1234567, and the product’s own name padded the same way. Then check storage: a salt is a random value stored with each user so that two equal passwords produce different hashes, and a deliberately slow algorithm such as Argon2, bcrypt or PBKDF2 makes every guess expensive for an attacker who steals the table. In a test database, the users table must therefore hold salted hashes, never plain text, and the password must not appear in logs or URLs.

#  Input (password is the only factor)   Expected
1  14 characters                         rejected: fewer than 15
2  15 characters                         accepted, and login works with it
3  64 characters                         accepted, and login works with it
4  65 characters                         rejected with a message, or accepted whole;
                                         never silently truncated
5  Arabic words with spaces              accepted (Unicode and the space)
6  Password1234567, product name padded  rejected: blocklist
7  paste, autofill, show password        all allowed
8  users table in a test database        salted slow hash, not the password

Try it: Password Generator Try it: String Length Counter Try it: Unicode Dataset Generator

Lockout, throttling and the second factor

Lockout disables sign-in for an account after a number of failed attempts, for a period or until someone unlocks it; throttling slows attempts instead, with a growing delay, a CAPTCHA after a few failed attempts, or a limit per account and per source address. SP 800-63B requires the verifier to limit consecutive failed attempts on one account to no more than 100. A lock that anyone can trigger by typing a victim’s e-mail with wrong passwords can itself be used for denial of service, so record how a lock is lifted and how long it lasts.

MFA adds a check from a different category, usually something the user has: a one-time code from an authenticator app, a security key, a code sent to a phone. Test the step as a small authentication of its own: a time-based code (TOTP) changes every 30 seconds by RFC 6238’s default, and a 6-digit code has only a million values, so the step needs its own attempt limit; SP 800-63B also requires each one-time code to be accepted only once while it is valid. It treats the phone network as a restricted channel, so record codes sent by SMS as a risk, not a defect.

  • Count the wrong passwords it takes to trigger the throttle or lock, using a second test account, and check that the counter lives on the server: a page reload, a new browser session or cleared cookies must not reset it, only a successful sign-in or the end of the documented lock period.
  • Repeat the failures through every entry point — web form, mobile API, reset page, MFA step. The limit must hold everywhere, and the message must not reveal whether the account exists.
  • MFA: request the post-login page or API right after the password step without a code; send the same code twice; try a code that expired several time steps ago, which must fail once it is older than the window the team documents (RFC 6238 recommends accepting at most one earlier time step for network delay, and a set limit for clock drift); check that recovery codes are single-use, that "remember this device" expires, and that a password reset does not skip the second factor.

Try it: API Request Builder

Worked example: reset flow and enumeration on a shop login

A shop has a login, a registration form and a "Forgot password" page. You have two test accounts, qa.one@example.com and qa.two@example.com, and a throwaway address that never registered. Send the same request with a known and an unknown address to each of the three pages, capturing each pair with the intercepting proxy from the previous lesson or an HTTP client, and compare everything in the two responses: message, HTTP status, redirect target, body size and time taken. The login pair below fails at the first comparison.

  • Login: 401 for a known address and 404 for an unknown one is user enumeration. Expected: the same status, the same message and no noticeable difference in timing (an unknown account often answers faster because the server skips the slow hash comparison).
  • Registration: "This e-mail is already registered" is enumeration too. Some teams accept it for usability; the alternative is to always answer "check your inbox" and e-mail either a welcome or a "you already have an account" message. Record which one the product chose.
  • Forgot password: the page must answer identically for both addresses. Then test the token it e-mails: set a new password through the link and open it again (it must fail, because the token is used), wait past its expiry, alter one character of the token, and request two resets to see whether the first link still works. The reset page must not leak the token from its URL in the Referer header, in which the browser sends the address of the page a request comes from: it should set Referrer-Policy: no-referrer, as the OWASP Forgot Password Cheat Sheet advises, so that no third-party script, image or link on that page receives the token.
  • After a successful reset: the old password must stop working, sessions opened before the reset must end (or, at a minimum, the user is offered the option to end them), and the user must be notified that the password changed.
# Known test account, wrong password
POST /api/login HTTP/1.1
Host: staging.shop.example
Content-Type: application/json

{"email": "qa.one@example.com", "password": "Not-the-password-1"}

HTTP/1.1 401 Unauthorized
{"error": "Invalid e-mail or password"}

# Same request, an address that never registered
POST /api/login HTTP/1.1
Host: staging.shop.example
Content-Type: application/json

{"email": "never.registered@example.com", "password": "Not-the-password-1"}

HTTP/1.1 404 Not Found
{"error": "No account for this e-mail"}

Try it: API Response Comparator Try it: Temporary Email

Common mistakes

  • Testing only the browser form and forgetting the API, the mobile client and the reset page, where the same limits and messages must hold.
  • Reading "Wrong password" and "Unknown e-mail" as two correct messages instead of one enumeration defect.
  • Passing a lockout without asking how it is lifted and whether anyone can lock any account at will.
  • Checking that a reset link works, but not that it stops working after use, after expiry, after a second request and after a password change.
  • Reporting the absence of composition rules or 90-day rotation as a defect: SP 800-63B rules out both, so a policy without them follows the guideline rather than breaking it.
  • Running password-guessing or lockout tests against production, or against any system you have no written permission to test.

The QA perspective

On a team, authentication testing starts when the login, sign-up or reset story is written: ask for the policy in numbers (minimum and maximum length, attempt limit, lock duration, token lifetime, MFA options) and the edition of SP 800-63B it follows, and put them into the acceptance criteria, because "secure login" cannot be tested. Run these checks on every release that touches identity code and keep the policy and enumeration cases in the regression suite: they break quietly when a developer adds a more helpful error message.

Report findings as behaviour, not blame: the exact requests, both responses side by side, the number of attempts before the lock, the token lifetime as measured. Rate severity by what an attacker gains: enumeration alone is usually minor or major, depending on what the accounts are worth; a reset token that never expires or an MFA step that can be skipped is critical. Treat test accounts, captured tokens and e-mail screenshots as sensitive data and keep them where the team keeps secrets, not in the ticket.

Practice exercise

Write the test cases for a forgot-password flow

Your task

A job portal’s requirement reads: "Users can reset their password from the login page by entering their e-mail. They receive a link that is valid for 30 minutes. The new password must follow the password policy." Write the authentication test cases for this flow, including what to compare for enumeration and what to try with the link.

Do it on paper, or against the reset flow of an application you own, with a throwaway address from the temporary e-mail tool as the unknown account.

Steps
  1. List the requests involved (the reset form, the e-mailed link, the new-password form) and the inputs of each.
  2. For each request, write the known-account and unknown-account pair and what must be identical.
  3. Write the token tests: single use, 30-minute expiry, two requests in a row, a modified token.
  4. Write what must be true after the reset: old password, open sessions, notification, and the policy on the new password.
Show the model answer
Model answer

Enumeration: for qa.one@example.com and for an address that never registered, the reset form must return the same status, the same message ("If this e-mail is registered, we have sent a link") and a response time of the same order; check the registration form the same way.

Token: once the new password has been set, opening the link again fails; a link used at 29 minutes works and one opened after 31 minutes fails; a second request invalidates the first link, or the product documents that both stay valid until used; a token with one character changed fails; and the page the link opens sets Referrer-Policy: no-referrer, so no third-party script, image or link on it receives the token in the Referer header.

After the reset: the old password is rejected; a session opened in another browser before the reset is signed out, or the user is offered the option to sign out every other session; the user receives a "your password was changed" e-mail; the new password is checked against the policy from this lesson (14 characters rejected if the password is the only factor, Unicode accepted, a blocklisted value rejected); and a user with MFA still needs the second factor, otherwise access to the inbox alone takes over the account.

Frequently asked questions

Is authentication testing the same as penetration testing?

No. A penetration test is a time-boxed, authorised attack by specialists that goes as far as it can. Authentication testing here is part of ordinary QA: you check the policy, lockout, MFA and reset behaviour against a written expectation, on every release. The two complement each other, and both need written permission.

Should I report "This e-mail is already registered" on a sign-up form as a defect?

Report it as a finding and let the product decide. It is user enumeration, but many products accept it on sign-up for usability. The login and forgot-password pages have no such excuse: there the response must be identical for known and unknown accounts.

The app enforces one uppercase letter, one digit and a change every 90 days. Is that wrong?

It does not follow NIST SP 800-63B: revision 4 forbids composition rules and periodic rotation (the 2017 edition advised against them) and relies on length, a blocklist and MFA instead. It is a defect only if the requirement adopts SP 800-63B; otherwise raise it with the product owner as a usability and security recommendation.

How do I test MFA without a real phone?

Enrol a test account with an authenticator app, or compute the codes with a TOTP library from the secret shown at enrolment; the codes are calculated, not sent. For SMS, ask the developers how the test environment exposes the code, often in a log or a test message inbox. Never test with a customer’s number.

References

Practise with Mutqan tools

Compose and send HTTP requests (method, URL, headers, auth, body) and inspect the response — a lightweight Postman in your browser.

API Testing

Compare two API responses (e.g. staging vs production) and list every structural and value difference.

API Testing

Generate strong random passwords with adjustable length and character classes, plus a strength meter.

Security

Measure string length in characters, code points, UTF-8 bytes and UTF-16 units.

Formatting

Generate Unicode test strings across scripts (Arabic, Devanagari, CJK, Cyrillic), emoji, combining marks and normalisation forms.

Test Data

Generate a throwaway email address and read what arrives, on the same page. The address is released when you stop using it.

Utilities New

Go deeper

Your progress on this topic

Part of the QA Roadmap