You cannot test what you cannot reach. Authenticated API testing means signing in the way a real client does, and never letting a secret touch the conversation.
Dennismary Chidiebere24 June 20265 min read
Operator Notes
Get the next essay in your inbox.
Field notes from building, deciding and carrying the cost of clarity.
Point a scanner at a real API and most of what matters answers with a 401. The interesting surface (the endpoints that move money, change records, grant access) sits behind a login. A tool that cannot sign in tests the lobby and declares the building secure.
That is the quiet failure of a lot of API testing. It runs, it produces a report, and the report is about the handful of public endpoints nobody worried about. The authenticated surface, where the actual risk lives, was never reached. A green result that only covers the parts anyone can see is not a green result. It is a blind spot with a badge.
If you want to test what matters, you have to get through the door the way a real client does.
Logging in is not one thing
"Authenticate the scan" sounds like a single checkbox. It is not. It is a family of flows, and a tool that handles one and not the others simply fails on most real APIs.
A multi-step login carries state between requests: a cookie set here, a CSRF token that has to be read from one response and replayed in the next. Miss that and the second step rejects you. Plenty of APIs gate the whole thing behind a one-time code: a six-digit number sent out of band that you must produce before anything else proceeds. Others use time-based one-time passwords (TOTP(opens in a new tab)) where you compute the current code from a shared secret on a thirty-second clock. And a large share speak OAuth2(opens in a new tab), in several grant types, often with refresh tokens that rotate on every use, so the token you hold is only good once.
Dino's flow runner handles all of it: multi-step logins with cookies and CSRF carried across steps, one-time codes and TOTP, OAuth2 across client-credentials, password and refresh-token grants including rotating refresh tokens and public clients. Not because variety is impressive, but because an API testing tool that only logs in the easy way is a tool that only tests the easy APIs.
One token is not enough
Even once you are in, a single identity is not enough to test authorisation, and authorisation is usually where the real defects hide.
The question "can a standard user reach the admin endpoint?" cannot be answered by a scan that only holds an admin token. You need a token per role, and you need to drive the same operations as each one, so the tool can actually see where the boundary leaks. An endpoint that quietly returns another tenant's data to a lower-privilege caller is invisible unless you are testing as that caller. So the runner authenticates role by role, and authorisation gets tested as a matter of behaviour rather than assumed from the spec.
The one-time code problem
A one-time code is designed to be unguessable and to arrive out of band: in an inbox, on a device. That is the whole point, and it is exactly what makes it awkward to automate honestly.
The clean way to handle an email code is an isolated inbox that receives the message, binds the code to the specific scan that is waiting for it, and hands it to the runner, the same path a mature scanner uses to get behind a login wall. Isolated, so one scan's code cannot bleed into another's. Bound to the tenant, so there is no ambiguity about which run the code belongs to. And built honestly: the inbox is a real recipient, not a system pretending to be mail infrastructure it is not. The code gets intercepted the way a legitimate client would receive it, not through a back door.
Never die on expiry
A real scan of a real API is not a single request. It is a long run: operations in the hundreds, time measured in tens of minutes. Tokens were not designed to last that long. So the most important property of the auth layer is not getting in. It is staying in.
The failure mode is brutal and silent. A token expires forty minutes into a ninety-minute scan. Every request after that returns 401. The scan does not crash; it keeps going, dutifully recording hundreds of "failures" that are not findings at all, just the sound of an expired session. You get a report full of false 401s that looks like the API is broken when it is the scanner that fell asleep.
Getting this right is unglamorous engineering. Refresh with a margin proportional to the token's lifetime, so you renew before expiry rather than after a failure. Retry transient faults with jittered backoff so a momentary hiccup does not cascade. And when authentication is genuinely lost, re-queue the work behind a fresh login rather than marking it failed. A ninety-minute scan has to defend its own session for ninety minutes, or everything after minute forty is noise.
Fail closed, never fabricate
Here is the load-bearing rule of the whole layer: a failed login must never become a clean authenticated pass.
It is a tempting shortcut. Authentication is flaky, the login hiccups, and the easiest way to keep the scan "green" is to let it proceed and quietly treat the unauthenticated responses as fine. That is how you ship a tool that reports success on a scan that never actually logged in. The auth layer has to fail closed: if it could not establish the identity it was asked to, it says so and stops, loudly. It never manufactures a token, and it never dresses up a login failure as a result. An honest 401 you surface is worth infinitely more than a fake pass you hide.
Secrets never touch the conversation
None of this is acceptable if the price is handling the customer's credentials carelessly. Getting in cannot mean the secret ends up in a chat transcript, a log line, or a vendor's database.
So in Dino the person enters a credential through a hosted handoff, never in the agent conversation that is onboarding the API. The database holds only an opaque, versioned reference, never the secret itself. The secret is released for a stated purpose, for a bounded time, and every grant is revocable. When anything is unclear, custody fails closed. And erasure is permanent, even after the organisation that owned the secret is gone. The runner can log in like a real client precisely because the secret it uses was handled like something that would hurt you if it leaked, because it would.
Reach is the prerequisite
Every claim a testing tool makes starts with one question: did it actually get to the thing it is testing? Coverage, findings, verdicts: all of it is worthless if the scan spent its time knocking on a locked door.
Signing in like a real client, staying signed in for the length of a real scan, testing as every role, and doing it without the secret ever touching the conversation: that is not a feature next to the testing. It is the ground the testing stands on.
You cannot verify what you cannot reach. So first, you get in, honestly.