Skip to content
apikey.tools
SIMTesting

Dummy API Request Authentication Sandbox

Step a request through the checks a gateway performs and see exactly where it fails.

SIMRuns in this tabInput not transmitted

Input

sec

0 simulates an already-expired credential.

Output

computing

Six checks, in order. The first failure decides the response, and the order is not arbitrary.

  1. Credential present — no header at all → 401
  2. Well formed — whitespace or a broken shape → 401
  3. Recognised — does not match a known key → 401
  4. Not expired → 401
  5. Scope granted403
  6. Source address allowed → 403

Why the order matters

Authentication before authorisation, always. You cannot decide what a caller may do until you know who they are.

The transition between check 4 and check 5 is where the status changes from 401 to 403, and it is exactly where implementations get it wrong. Up to that point the caller is unidentified. From that point they are identified and simply not permitted.

Move the "scopes granted" and "scope required" controls apart and watch the response become 403. That is the behaviour your API should have.

What each check catches

Well formed rejects a credential containing whitespace — almost always a copy-paste artefact or a shell variable that never expanded. This is worth checking separately because the resulting 401 is otherwise indistinguishable from a wrong key, and the fix is completely different.

Recognised is where the test-key-in-production error lands, which is the single most common cause of an unexpected 401.

Source address is last because it is cheap to evaluate but only meaningful once you know which key's allowlist to check.

Constant time, and no enumeration

Two things the simulation implies that your implementation must actually do:

  • Compare credentials in constant time. A comparison that returns early leaks the valid value byte by byte.
  • Do not reveal which keys exist. An unknown key and a revoked key must produce identical responses, or an attacker can enumerate.

Failing open

The most dangerous bug in this sequence is not returning the wrong status — it is returning 200. That happens when the credential lookup throws and the exception is caught by a handler that returns a default, or when an empty credential is treated as "no check required".

Test it: send an empty credential and confirm you get 401, not 200.

Frequently asked questions

Should I check the IP allowlist before the credential?

No. You need to know which key is being used before you can know which allowlist applies. Checking a global allowlist first is possible but gives a worse error and blocks legitimate keys from new locations before you can identify them.

What if a key is valid but the account is suspended?

403, with a distinct error code. The caller is authenticated; the account state forbids the action. Do not use 401 — the client would refresh a perfectly valid credential.

Should authentication happen in the gateway or the application?

Both, ideally. The gateway rejects obviously bad requests cheaply, and the application enforces authorisation because only it knows the resource-level rules. Never rely on the gateway alone — anything that reaches the application directly bypasses it.

How do I test all of these against a real API?

Use the cURL generator to build each malformed request, and the 401 and 403 simulator for the expected responses. Assert on the status code and on WWW-Authenticate, not just on failure.

More testing tools

Rehearse authentication before you ship it