Key takeaways
- A web application is observed at one place, the application. An API is observed at three: the gateway, the identity provider and the service behind them. Every phase of an API pentest is visible at some of those and invisible at the rest.
- Version probing is seen only at the gateway, token abuse only at the identity provider, and an object level authorisation sweep only at the service, and only if the service records who owns the object. A monitoring result read from one layer is a partial result.
- A replayed partner token is the case the layers have to be joined for. The identity provider knows which client it issued the token to, the gateway knows who presented it, and no rule fires unless the two records meet in one place.
- Score every hostile request on two axes: whether the layer blocked it and whether the record reached an analyst. Blocked and unseen is silent enforcement, allowed and unseen is the finding that costs, and both belong in the report.
- Agree the join before day one: tester source addresses, the token subjects the test will hold, a request identifier the tester sends and the gateway logs, the window, and a shared clock.
An API is watched from three places, and none of them sees the whole test
A web application is observed where it runs. The application receives the request, makes the decision and writes the log, so when a tester attacks it there is one place to look for the trace. An API is not built that way. The request meets a gateway first, which terminates the connection, validates the token and enforces the rate limit. The token was issued somewhere else, by an identity provider that the gateway trusts and the service never speaks to. The service behind both is the only component that knows which object the request touched and who owns it. Three components, three logs, and in most estates of any size three different owners.
That structure is what makes the detection result of an API pentest different from the same result on a web application. Each phase of the test is visible at some of the three layers and invisible at the rest. A monitoring result read from the gateway alone, or from whichever feed the gateway happens to send to the SIEM, is a partial result presented as a whole one. The question to settle before the test starts is not whether the API is logged. It is which layer would have recorded each thing the tester is about to do, and whether those records ever reach the same place.
What each layer sees of each phase of the test
The table sets out the main phases of an API pentest against the three layers, and it is worth reading by column as well as by row. The gateway column is full, because the gateway sees every request, but what it sees is shallow: a path, a status code and, if it validates the token rather than passing it through, a subject and a client identifier. The identity provider column is nearly empty, and the one phase it does see, token issuance and refresh, is the phase nothing else can. The service column carries the decisions, and only if the service writes them down.
Two rows deserve a note. Version and path probing, the step that finds the retired endpoint described in how an API pentest builds its endpoint inventory, reaches only the gateway. A path the gateway does not route never arrives at a service, so the run of 404 responses across version prefixes exists in exactly one log, and a gateway configured to drop error statuses before forwarding them has erased the only trace. An object level authorisation sweep is the opposite case. It reaches all three layers as ordinary traffic and is a finding in none of them unless the service records the owner of the object beside the subject that asked for it. The OWASP API Security Top 10 ranks that flaw first for the same reason it is hard to see: the control is absent, so there is no refusal to log.
| Test phase | Gateway records | Identity provider records | Service records |
|---|---|---|---|
| Version and path probing | A run of 404 and 405 responses across version prefixes from one source | Nothing | Nothing: an unrouted path never arrives |
| Token issuance and refresh abuse | Valid tokens presented normally | Scopes granted beyond the client's registration, a refresh token presented twice | Nothing |
| Object level authorisation sweep | Successful responses, one subject, identifiers walked in order | Nothing | The object's owner differs from the subject, if the owner is a field |
| Function level authorisation | A customer-scoped token on an administrative route, if routes carry a required scope | Nothing | The role check outcome, if the service performs one |
| Rate limit testing | 429 responses issued, or a burst that drew none | Bursts at the token endpoint | The full volume, if the gateway passed it |
| Mass assignment | Nothing, unless the request schema is validated at the edge | Nothing | A field written that the declared schema does not carry |
A replayed partner token is the case the layers have to be joined for
Machine clients are the part of an API estate that a web application does not have, and they change what a stolen credential looks like. A partner integration calls with a client-credentials token, at any hour, from an address the partner controls, at a volume no human generates. All of that is normal. So when the same token is presented from a second network, every layer that looks at it in isolation sees nothing wrong: the signature verifies, the expiry has not passed and the scope matches the route. The article on tokens, rate limits and logs under UAE rules set out why a token is three controls. This is what the third one looks like when it is missing.
The chain below is the general shape, and the reason to draw it is where the two useful records land. The identity provider wrote down which client it issued the token to and from where. The gateway wrote down who presented it and from where. Neither record is wrong and neither is enough. The detection is the comparison between them, and it only exists if issuance records and gateway access records reach one platform with a field in common, which is the token identifier or the subject. Where the gateway and the identity provider log to different platforms, as they do when different teams own them, that comparison has never been possible, and the test is the first time anyone notices. The control-side fix is a sender-constrained token, bound to the client's certificate or a proof-of-possession key, so that a copied token stops being a working one. The detection-side fix is the join, and it is cheaper.
- Identity provider → Log pipeline
Records issuing the token to one client, from one address
Client identifier, scope, issue time and source
- Attacker → API gateway
Presents the same token from a second network
Valid signature, unexpired, scope intact
- API gateway
Vulnerability
Verifies signature, expiry and scope, never the presenter
A bearer token with no binding to the client
- API gateway → Log pipeline
Logs a normal success under the partner's client identifier
Source address written, never compared with issuance
- API gateway → Attacker
Objective reached
Answers as the partner for the life of the token
Every call is inside the partner's scope
Four outcomes per request, and only one of them is fine
The scorecard for a web application, described in what your monitoring should catch during a web app pentest, asks one question of each hostile request: was it seen. An API needs a second axis, because the gateway enforces as well as observes. A rate limit that refused the tester's burst, a scope check that rejected a customer token on an administrative route, a schema validator that dropped an unexpected field: each is a control that worked, and each may have worked in silence. The grid below is the four outcomes. It is filled in per request and per layer, not per finding.
Two cells are the ones to argue about. Blocked and unseen looks like success and is not. The gateway refused the request, and nobody in the organisation would learn that an attacker was working through its endpoints, because a refusal that raises no record is indistinguishable from no attempt. Allowed and seen is the opposite case, and the better one: the record has the fields a rule needs, the control is missing, and the alert is a day's work away. Allowed and unseen is the cell the test exists to find, and on an API it is where authorisation sweeps and mass assignment land by default. The table in the previous section predicts which cell each phase will fall into before the test starts, which is a reasonable thing to ask a proposal to do.
Control outcome
Silent enforcement
Refused at the edge, and nobody would learn an attack was under way
Working as designed
Control and record both did their job. Note it and move on
The finding that costs
Served, and recorded nowhere useful. Fix the control, then the record
A rule waiting to be written
The record has what a rule needs; the control is the gap
Record reached analyst
Agree the join before day one, then fix by layer
The scorecard is only as good as the join between the tester's own record and yours, and on an API the join needs more than a source address. Before the first request the tester should hand over the source addresses the test will come from, the token subjects and client identifiers the test will hold, a request header carrying a per-request identifier that the gateway is configured to log, and the window. In return the tester keeps a timeline: every request, its identifier, the token used and the response. After the test each line is looked up in the gateway log, the identity provider's issuance log and the service log in turn. NIST SP 800-92 covers the log management side of that, including the clock synchronisation without which three timelines cannot be laid side by side.
The gaps then sort by layer, and the fixes differ in each. At the gateway: log the token subject and client identifier on every line, never the bearer string; log the route template beside the path; and keep the 404 responses, because version probing lives nowhere else. At the identity provider: export issuance, refresh and revocation events to the platform the gateway writes to, with the token identifier in both. At the service: write the object's owner and the authorisation decision beside the subject, which is the field list the OWASP logging cheat sheet sets out. Then the rules: a version prefix not called in a set period, one subject walking identifiers, subject and owner differing on a success, a token identifier presented from a source the issuance record does not know. Writing and proving those is a purple team session with the API test's own traffic as the replay set, and it is the cheapest one available, because the hostile requests already exist and are already timestamped.
Three layers, three owners: the join in Dubai and Abu Dhabi
In the United Arab Emirates the three layers are often three organisations, which makes the join a contractual question before it is a technical one. For a bank licensed by CBUAE running open finance endpoints, the party issuing or validating third-party credentials may not be the party running the gateway, and the services behind the gateway may belong to a vendor. The examiner reading the evidence afterwards asks whether a replayed third-party token would have been seen, and the honest answer depends on whether issuance records ever reach the bank's own monitoring. For a supplier to a Dubai government entity under DESC, the entity's gateway sees the supplier's endpoints being probed and the supplier's service sees the object level decisions. A test scoped to the supplier's side alone reports on one column of the table.
In Abu Dhabi the same split appears in health exchange integrations under ADHICS, where the exchange side validates credentials and the vendor's service holds the clinical record. The owner field beside the subject is the line a healthcare examiner reads first. Across both emirates the practical step is the same. Before the test, name which party holds each of the three logs, agree that all three will be read for the scorecard, and have the tester's timeline delivered in a form each party can query. A mobile app pentest of the client in front of these endpoints sends the same traffic through the same gateway, so where the app and the API are tested together the join is agreed once and the result is read for both.
Frequently asked questions
Where should an API pentest be detected: the gateway, the identity provider or the service?
At all three, because each records a different phase. The gateway alone sees version and path probing, since an unrouted path never reaches a service. The identity provider alone sees token issuance and refresh abuse. The service alone can see an object level authorisation flaw, and only if it records the object's owner beside the requesting subject. A monitoring result read from one layer is partial.
How do you detect a stolen or replayed API token?
By comparing the identity provider's issuance record with the gateway's access record for the same token identifier. Issuance says which client the token went to and from where; access says who presented it and from where. A token presented from a source the issuance record does not know is the signal. The control-side fix is a sender-constrained token bound to the client's certificate or key, so a copied token no longer works.
What should the tester provide so the SOC can score an API pentest?
Source addresses, the token subjects and client identifiers the test will use, a per-request identifier sent in a header the gateway logs, the test window, and a shared clock. During the test the tester keeps a timeline of every request with its identifier and response. Afterwards each line is looked up in the gateway, identity provider and service logs and scored as blocked or allowed, and seen or unseen.
Is a request the gateway blocked still a detection gap?
It can be. A rate limit or scope check that refuses a request has enforced the control, but if the refusal raises no record, nobody learns an attacker was working through the endpoints. Blocked and unseen is silent enforcement. It is a lesser finding than allowed and unseen, but it belongs in the report, because those refusals are what would tell you an attack had started.






