Testing authentication
An endpoint that answers correctly to the right user is half tested. The other half is that it refuses everyone else. Mistakes here are the costly kind, so every protected endpoint deserves a few deliberate attempts to get in without permission.
If the terms are new, read How APIs know who you are first.
Test only what you are allowed to
Run these tests against your own API, a test environment you have been given, or a mock. Probing someone else's system without permission is not testing.
The cases every protected endpoint needs
| Case | Send | Expect |
|---|---|---|
| Valid credentials | A good token | 200 |
| No credentials | No Authorization header at all | 401 |
| Malformed credentials | Bearer with nothing after it, or a random string | 401 |
| Expired token | A token past its time | 401 |
| Wrong scheme | Basic credentials where a Bearer token is expected | 401 |
| Right user, wrong permission | A normal user's token on an admin endpoint | 403 |
| Someone else's data | User A's token asking for user B's order | 403 or 404 |
The last two are authorization tests. They are the ones most often missing, and the ones that find real leaks: an API that checks you are signed in but not that the order is yours.
Sign in once, reuse the token
Set this up before anything else, so every later request has a fresh token.
Create a Sign in request as the first request in the collection. In its Captures tab, add:
texttoken = body.tokenIn the collection's settings, set the auth type to Bearer Token with the token
{{token}}. Requests inside can then inherit it. See Authorization.Run Sign in once. From then on, every request that inherits the auth sends the token.
Keep the credentials themselves in variables, marked secret: {{email}} and {{password}}. See Variables and environments.
Build the negative cases
For each protected endpoint, duplicate the working request and change only its auth.
| Case | How to make it in ApiWorkbench |
|---|---|
| No credentials | Set the request's auth type to No Auth |
| Malformed token | Bearer Token with the value not-a-real-token |
| Wrong scheme | Basic Auth with any username and password |
| Another user's data | Sign in as a second test user into {{tokenB}}, and use that on user A's resource |
Give each a Status equals 401 (or 403) test, and a JSON field equals on the error code if the API has one.
The Shop API sample has three ready-made: Who am I, without a token, Place an order without a token and Sign in with a wrong password.
Test the sign-in endpoint itself
| Case | Expect |
|---|---|
| Correct email and password | 200 and a token |
| Wrong password | 401 |
| Unknown email | 401, with the same message as a wrong password |
| Missing password | 400 or 401 |
| Empty body | 400 |
The third row matters. If "unknown email" and "wrong password" get different answers, anyone can find out which email addresses have accounts. A test can pin that: both requests check for the same error value.
Also read the successful response critically. It should not contain the password, a password hash, or more about the user than the client needs.
Expiry and refresh
Tokens end. Two things to check:
- An expired token gets 401, not a 500 and not a 200. Getting one usually means waiting, asking the server team for a short-lived test token, or testing against a mock.
- Refresh works. If the API has a refresh endpoint, test it like sign-in: valid refresh token gives a new access token; an invalid one gives 401.
With OAuth 2.0, ApiWorkbench obtains and refreshes tokens for you. See OAuth 2.0.
Using a mock
Mock Studio can stand in for the lock:
- Turn on Require auth header for an endpoint. A request without the header gets 401 before any response is chosen.
- For a wrong-password case, use Conditional rules on the body, as the Shop API's Login does.
- For an expired-token case, add a rule on the header: Header
AuthorizationequalsBearer expiredreturns 401. Then your client's handling of expiry can be tested on demand.
A mock checks that a header is present, not that a token is genuine. It is the right tool for testing how a client behaves when refused. Whether the real API enforces its rules can only be shown against the real API. See Auth, CORS and project settings.
What to look for beyond the status
- Secrets in URLs. Tokens and keys in a query string end up in logs and browser history. Prefer headers.
- Secrets in errors. An error should never echo the credential back.
- Plain HTTP. A protected endpoint that answers over
http://exposes every token. WWW-Authenticateon a 401. The standard says it should be there; check with Header contains.
Keep your own secrets safe
- Write
{{token}}in requests; keep values in an environment, marked secret. - Review an export before sharing it. See Export and share.
- Use accounts created for testing. Never a real person's.