A support console calls the accounts API under its own service identity - what does the threat model lose?
answer
- Two identities travel on one call
- Who does the receiver actually see
- One account must serve every caller
- Union of every agent's access
- A header nobody verifies is not identity
basics
~10 sThe agent's identity dies at that boundary. Downstream sees one caller holding the union of every agent's access, cannot make any per-agent decision, and records one name for every read, so attribution is lost.
solid answer
~50 sI annotate both ends of the call, because they carry different principals. Inbound: a support agent, authenticated, scoped to the queue they own. Outbound: `console-svc`, read access to every account. Where the labels differ is where the user identity terminates - that is the trust boundary. Three consequences follow. The console's effective privilege is the union of what any agent might ever need, so a malicious agent who can drive it reaches beyond their own queue. The accounts API cannot enforce anything per agent, because the agent never arrives. And every downstream record names `console-svc`, so attribution of who read a customer's personal data is lost at the crossing. The design choice - propagate a user assertion verified downstream, or keep the service identity and hold the per-agent decision in the console - is legitimate either way, but the diagram has to state which one it is.
go deeper
Be ready to say that a service can call another service as itself, and that the person who started the request is then invisible to the receiver. Recognising the two identities is the whole ask at this level.
Explain the mechanics of the crossing: what the receiver actually sees, why a shared service account ends up with the union of everyone's access, and why an unverified user field in a request is not the same as a verified identity.
Demonstrate that you have operated this. Mark both principals on the flow, name the three losses - union privilege, no per-user decision downstream, collapsed attribution - and say which design you would take for the system in front of you and why.
Own the tradeoff across a portfolio: propagating identity everywhere costs a verification path and a trust chain, while concentrating decisions upstream costs blast radius. Be ready to argue where each is right and how the choice is recorded so teams stop re-deciding it.
## Two identities on one call When a component acts because a person asked it to, there are two identities in play. The **service identity** is what the component presents to whatever it calls next: a machine account such as `console-svc`, with grants of its own. The **propagated user identity** is the identity of the person who caused the call, carried across the hop in some verifiable form so that the receiver can evaluate it. A data-flow diagram that annotates only the process, and not the flow leaving it, silently assumes these are the same. They usually are not. The worked case: a customer-support console. An agent signs in, is authorised for the queue they handle, and opens a customer record. The console calls the accounts API - and it calls as itself, holding read access to every account in the product so it can serve any agent. On the diagram, the inbound flow is annotated *support agent, scoped to own queue*; the outbound flow is annotated *`console-svc`, read any account*. Those two labels differ, so the console's outbound edge crosses a trust boundary, and it is the point where the user identity ceases to exist. ## What is actually lost **Effective privilege becomes a union.** A service identity must be able to serve every legitimate request any caller might make, so its grant is the union of all of them. That union is the privilege inherited by anything that can drive the console: a malicious agent using it as intended-but-abusively, or anyone who compromises the console process. The agent's own scope stops constraining what reaches the accounts API. **The downstream cannot decide per user.** The accounts API only ever sees one subject. It can decide whether the console may call it; it cannot decide whether *this agent* may see *this customer*, because it was never told. That is not a bug in the API - it is a structural fact created by the boundary, and it means the per-agent decision must live somewhere else or nowhere. **Attribution collapses.** Everything the accounts API records about the call names `console-svc`. Reconstructing who read a given customer's personal data now depends entirely on records the console itself kept, on the other side of the boundary, and on those records still being joinable to the downstream ones. If the console is the very component under suspicion, the evidence and the suspect share a trust zone. The adversary worth naming here is not an anonymous internet attacker: it is a support agent with legitimate access and a reason to look up someone they should not. The assets are customer personal data and the ability to say afterwards who touched it. ## The two honest designs, and how each is annotated **Propagate the user identity.** The console forwards an assertion identifying the agent, minted by something the accounts API trusts and verified on arrival. The outbound flow is then annotated with two principals - `console-svc` as the caller, plus the agent as the subject on whose behalf it acts - and the per-agent decision can be made downstream. The model must then ask what stops a different caller from presenting an assertion for an arbitrary agent, which is a question about who can mint and who verifies, not about who forwards. **Keep the service identity and decide upstream.** The console holds the per-agent decision itself and calls downstream as a trusted component. This is defensible, common, and sometimes the only option with a system you do not control. What the model must record is the consequence: the console is now the only place the per-agent rule exists, so its own compromise or misuse is unconstrained downstream, and the boundary drawn at its outbound edge separates *many scoped agents* from *one broad principal*. Both appear on the diagram as annotations, not as prose in an appendix. A reviewer looking at the picture should be able to see, without asking, whether the agent survives the hop. ## The trap: an identity that nobody verifies The most common half-measure is a user identifier passed in a header or a request field that the downstream reads and trusts. That is not propagated identity; it is an unverified claim from a caller that already holds broad privilege. It buys convenience for records and nothing at all for decisions, because anything that can reach the accounts API as `console-svc` can also choose what to put in that field. The modelling question to ask out loud is *who could set this value, and does anything check it* - and if the answer is nobody checks, annotate it as a hint rather than an identity. ## What the interview is testing Handed an architecture at a whiteboard, the candidate who marks both principals on the flow and says *the user identity dies here, and this is what dies with it* has demonstrated the whole skill. The weak version notices only that the call leaves the console.
- Both designs can be defended. How do you record the choice on the diagram itself?Annotate the outbound flow with the identity presented and whether the user identity is carried and verified, then mark the element where the per-user decision is made. A reviewer should see the answer in the picture. When the decision sits upstream of the boundary, that element inherits the whole weight of the rule, and the annotation is what makes that inheritance visible instead of assumed.
- The console does forward the agent's user id in a request header. Does that close the gap?Only if the receiver can verify it and nothing else on that path can set it freely. An unverified field from a caller that already holds broad access is a convenience for record-keeping, not an identity for decisions. Ask who can mint the value, who checks it, and what happens if it names a different agent - if there is no check, annotate it as a hint.
- What does this cost you months later, during an investigation?Downstream records name the service, not the agent, so the question of who read a particular customer's data can only be answered from the console's own records - inside the trust zone you may be investigating. That is worth stating as a modelling consequence when the boundary is drawn, not discovered when someone asks the question for real.
saying these in an interview costs you the question
- Says the downstream is safe because only the console can reach it
- Treats a user id in a header as an identity without asking who sets it
- Assumes the service account's access equals the calling agent's access
- Calls the service identity acceptable because the call is internal
- Draws the boundary at the network hop rather than where identity changes