API Testing

API Test Cases for Login: 40 Cases with Expected Results

Parveen Kumari11 min read

Quick answer

Login API test cases check that valid credentials return a token with the right claims and expiry, that every invalid or missing input returns a clear 4xx error, and that the endpoint protects accounts: identical errors for unknown users and wrong passwords, lockout or 429 after repeated failures, HTTPS only, and logout that really ends the session.

Reviewed by Rishi Gaurav

Share:
Login API test case pack diagram: a POST /login request branching into positive, negative, validation, security-behaviour and session test cases with expected status codes

Part of the API Testing hub. Start with the pillar guide: What is API testing? Types, tools and how it works.

Login is the one endpoint every user hits and every attacker tries, so its test suite has to prove two things at once: that real users get in quickly, and that the endpoint gives nothing away to anyone else. This pack gives you 40 login API test cases with inputs and expected results, grouped the way a reviewer will read them. It is part of our API testing fundamentals guide, and if you want more patterns beyond login, the API testing examples post covers 22 more.

The cases assume a typical JSON login endpoint, POST /api/v1/auth/login, that accepts a username (or email) and password and returns an access token, optionally with a refresh token. Adjust paths, field names and status codes to what your API documents: the expected result should always come from your spec or requirements, never from what the API happens to return today.

What a login API test suite must cover

Five groups, each answering a different question:

GroupQuestion it answersCases
PositiveDo valid users get a working token?LGN-001 to LGN-006
NegativeAre wrong credentials and bad account states refused?LGN-007 to LGN-014
Input validationDoes malformed input get a clean 4xx, never a 500?LGN-015 to LGN-024
Account protectionDoes the endpoint avoid leaking information and resist repeated attempts?LGN-025 to LGN-033
Session and tokenDo tokens expire, refresh and revoke as documented?LGN-034 to LGN-040

Two rules apply to every case. First, a login endpoint must never return 500: any input a client can send should map to a documented 4xx. Second, assert the whole contract, not just the status code: response body shape, headers such as Cache-Control: no-store, and the absence of anything sensitive.

Positive login test cases

IDTest caseInputExpected result
LGN-001Valid username and passwordRegistered user, correct password200; body has access_token, token_type: Bearer, expires_in
LGN-002Valid email as the login identifierRegistered email, correct password200; same token shape as LGN-001
LGN-003Identifier is case-insensitive where documentedUser@Example.com vs user@example.com200 for both if the spec says emails are case-insensitive
LGN-004Token works on a protected endpointToken from LGN-001 on GET /me200; returns the same user
LGN-005Token claims are correctDecode token from LGN-001sub is the user id; aud, iss and exp match the spec
LGN-006Response is not cacheableAny successful loginCache-Control: no-store; no token in a Set-Cookie without Secure and HttpOnly

LGN-004 matters more than it looks: a suite that only checks for a 200 and a token-shaped string will pass even when the token is signed with the wrong key.

Negative login test cases

IDTest caseInputExpected result
LGN-007Wrong passwordRegistered user, wrong password401; generic error body
LGN-008Unknown usernameUsername that does not exist401; same body and similar timing as LGN-007
LGN-009Correct password, wrong userAnother user's password401
LGN-010Disabled or suspended accountValid credentials of a disabled user401 or 403 as documented; no token
LGN-011Unverified account (if verification is required)Valid credentials, email not verified403 with a documented error code
LGN-012Password with different caseCorrect password, case changed401 (passwords are case-sensitive)
LGN-013Expired password (if the policy exists)Valid but expired passwordDocumented response, e.g. 403 with password_expired
LGN-014Wrong HTTP methodGET /api/v1/auth/login405 with an Allow: POST header

Input validation test cases

IDTest caseInputExpected result
LGN-015Missing usernameBody without username400 or 422; error names the field
LGN-016Missing passwordBody without password400 or 422; error names the field
LGN-017Empty body{}400 or 422
LGN-018Empty stringsusername: "", password: ""400 or 422, not 401
LGN-019Wrong typesusername: 123, password: true400 or 422
LGN-020Oversized values10,000-character password400 or 413; response time stays normal
LGN-021Malformed JSONTruncated JSON body400
LGN-022Wrong content typeForm-encoded body when only JSON is accepted415
LGN-023Unexpected extra fieldsBody with role: "admin" addedField ignored or 400; never changes the session's privileges
LGN-024Leading and trailing whitespace" user@example.com "Behaves as documented (trimmed or rejected), consistently

Generate API test cases from your spec (free)

Free, runs in your browser, no sign-up needed.

LGN-020 and LGN-023 are where real defects hide. Oversized passwords can make slow hashing algorithms expensive, and mass-assignment bugs let a client set fields it should never control.

Account protection test cases

These cases verify the protective behaviour your requirements specify. Run them only against your own non-production environment and dedicated test accounts.

IDTest caseInputExpected result
LGN-025Generic error messageCompare responses of LGN-007 and LGN-008Identical status, body and error code; no "user not found"
LGN-026Lockout after the configured thresholdN consecutive wrong passwords for one test accountAccount locked or challenged as documented; correct password now refused until unlock
LGN-027Lockout resets after a successN−1 failures, then a correct login200; failure counter resets
LGN-028Rate limit per clientRequests above the documented rate from one client429 with Retry-After
LGN-029Lockout does not reveal account existenceRepeated failures for an unknown usernameSame responses as for a real account
LGN-030HTTPS onlyCall the endpoint over plain HTTPRefused or redirected; credentials never accepted over HTTP
LGN-031Credentials not accepted in the URLUsername and password as query parameters400; credentials never logged
LGN-032No secrets in the responseAny login responseNo password, hash, salt or internal ids beyond what the spec lists
LGN-033Login events are auditedSuccessful and failed loginsAudit log records time, account and outcome, never the password

If your API uses multi-factor authentication, add cases for a missing second factor, an expired code and a reused code, each returning a documented 4xx without issuing a full session token.

For the wider picture of authentication and authorization testing (roles, object-level access, OAuth flows) see how to test API authentication and authorization and the API security testing checklist.

Generate the spec-derived cases automatically. Paste your OpenAPI spec into the free API test case generator to get missing-field, wrong-type, boundary and missing-or-invalid-auth cases for every operation as a CSV, then add the business-rule cases from this pack.

Session and token test cases

IDTest caseInputExpected result
LGN-034Token expires as documentedCall /me after expires_in has passed (use a short-lived test config)401
LGN-035Refresh returns a new access tokenValid refresh token on POST /auth/refresh200; new access token with a later exp
LGN-036Refresh token rotation (if documented)Reuse an already-used refresh token401; token family revoked as documented
LGN-037Logout revokes the sessionPOST /auth/logout, then call /me with the same token401 if tokens are revocable; documented behaviour otherwise
LGN-038Tampered token is rejectedChange one character in the token signature401
LGN-039Token for another audience is rejectedToken issued for a different service401
LGN-040Password change ends other sessions (if required)Change password, then use an old token401

Automating the pack with pytest

The cases translate directly into parametrised tests. This snippet covers a slice of the negative and validation groups and shows the generic-error check; it uses requests and reads the base URL and test credentials from the environment.

import os
import pytest
import requests

BASE = os.environ["API_BASE_URL"]          # e.g. https://staging.example.com/api/v1
USER = os.environ["LOGIN_TEST_USER"]
PASSWORD = os.environ["LOGIN_TEST_PASSWORD"]
LOGIN = f"{BASE}/auth/login"


def login(body, **kwargs):
    return requests.post(LOGIN, json=body, timeout=10, **kwargs)


def test_lgn_001_valid_login_returns_token():
    r = login({"username": USER, "password": PASSWORD})
    assert r.status_code == 200
    body = r.json()
    assert body["token_type"].lower() == "bearer"
    assert body["expires_in"] > 0
    assert "no-store" in r.headers.get("Cache-Control", "")
    me = requests.get(f"{BASE}/me", headers={"Authorization": f"Bearer {body['access_token']}"}, timeout=10)
    assert me.status_code == 200


@pytest.mark.parametrize(
    "case_id, body, expected",
    [
        ("LGN-015", {"password": "x"}, {400, 422}),
        ("LGN-016", {"username": "someone"}, {400, 422}),
        ("LGN-017", {}, {400, 422}),
        ("LGN-018", {"username": "", "password": ""}, {400, 422}),
        ("LGN-019", {"username": 123, "password": True}, {400, 422}),
    ],
)
def test_validation_cases(case_id, body, expected):
    r = login(body)
    assert r.status_code in expected, case_id
    assert r.status_code != 500, case_id


def test_lgn_025_same_error_for_unknown_user_and_wrong_password():
    wrong_password = login({"username": USER, "password": PASSWORD + "-wrong"})
    unknown_user = login({"username": "no-such-user@example.com", "password": "whatever"})
    assert wrong_password.status_code == unknown_user.status_code == 401
    assert wrong_password.json() == unknown_user.json()

Free ZIP: 5 CSV packs + guide

API Test Case Library

159 ready-made API test cases in five CSV packs: login, pagination, search and filter, auth (API key, JWT, OAuth 2.0) and errors. Copy, adapt, run.

Download Free

Keep lockout cases (LGN-026 to LGN-029) in a separate test module that runs serially, with a fixture that unlocks the test account in teardown, so they never make the rest of the suite flaky.

Common mistakes in login API testing

  • Only testing the happy path. One positive case and one wrong-password case is not a login suite; most login defects are validation and session defects.
  • Asserting on error text. Assert on status codes and documented error codes; human-readable messages change.
  • Hard-coding thresholds. Read lockout and rate-limit values from configuration so the tests follow policy changes.
  • Running account-protection cases against shared accounts. Use dedicated test users and reset them in teardown.
  • Never calling a protected endpoint. A token-shaped string is not proof that authentication works.

Download the login test case pack

The full pack is available as a CSV with the columns ID, group, test case, input, expected result, type and priority: download api-test-cases-login.csv. Import it into your test management tool or use it as the backlog for automation.

Shift-Left API generates the spec-derived part of this pack (missing and invalid fields, boundaries and auth-missing or auth-invalid cases) from your OpenAPI spec and runs it in CI; the business-rule cases above are the ones your team adds on top. If you want to try that on your own API, the free developer licence covers it.

API test cases for login: FAQ

What are the test cases for a login API?

Cover five groups: positive cases (valid credentials return 200 and a token), negative cases (wrong password, unknown user, disabled account), input validation (missing fields, wrong types, oversized values, wrong content type), account protection (identical error messages, lockout or rate limiting after repeated failures, HTTPS only) and session cases (token expiry, refresh, logout, reuse after logout).

What status code should a failed login return?

401 Unauthorized for wrong credentials, with the same body whether the username exists or not. Use 400 or 422 for malformed requests, 423 or a documented 401 variant for locked accounts, and 429 Too Many Requests with a Retry-After header when a rate limit is hit.

How do you test login lockout without locking real users?

Run lockout cases against dedicated test accounts in a non-production environment, read the threshold from configuration rather than hard-coding it, and reset the account in test teardown through an admin API or a database fixture so the suite stays repeatable.

Should login tests check the token contents?

Yes. Decode the token (without trusting it) and assert the subject, audience, issuer and expiry match what your API documents, and that no sensitive fields such as the password hash appear in it. Then call a protected endpoint with it to prove it actually works.

Can login API test cases be generated automatically?

Partly. A generator can derive missing-field, wrong-type, boundary and missing-or-invalid-auth cases from an OpenAPI spec. Business rules such as lockout thresholds, generic error messages and session revocation still need cases written from your requirements.

Sources and further reading

Ready to shift left with your API testing?

Import your OpenAPI spec and get a runnable test suite, in our cloud or on your own infrastructure.