- Published on
The model should not choose whose credentials it uses
- Authors

- Name
- Mehdi Akiki
Article · Through the layers
The tool accepts a user ID and an operation. The model chooses both. The backend loads that user's provider token and performs the operation.
That is an authorization boundary controlled by generated arguments.
The model can help select a target account when the product supports several accounts. It cannot grant itself access to whichever account it names. Credential resolution belongs to trusted application code.
Keep identity outside the generated arguments
The authenticated request establishes the actor. The server resolves the actor's allowed accounts and the authority for the requested action. Only then should it obtain the relevant secret.
A Rust interface can make the separation visible:
pub struct Context {
actor: String,
tenant: String,
account: String,
workflow: Workflow,
key: String,
revision: u64,
deadline: u64,
remaining: usize,
request_id: String,
}
ToolArguments can come from model output after validation. ActorContext must come from the trusted session path. A function that deserializes both from the same model-generated JSON has only renamed the problem.
Treat target_account_id as a requested resource. Check it against the actor's current grants for the exact action. Do not resolve a more privileged default account when lookup fails.
The authorization-through-tool-calls article follows this context across a workflow. The credential lookup is a particularly important point because the selected secret can make an otherwise denied action succeed.
Recheck at the effect boundary
A long workflow may pause in a queue or wait for approval. Access can change during that time. Revalidate the relevant grant before the external effect, using the policy's freshness requirements.
Background jobs need an explicit identity too. They may act for a user, a service account, or a bounded delegated grant. Record which one and why. “The job has no browser session” is not a reason to use an unrestricted administrator token.
OWASP's authorization guidance recommends checking permission for each request. Carrying an actor ID in a trace is useful, but it is not the same as enforcing that actor's authority.
Test the secret that was actually selected
Use a fake credential store that returns distinguishable test handles for different accounts. Ask for an allowed account and assert that the expected handle reaches the mock transport.
Then request another user's account, an unknown account, and an account whose grant was revoked after planning. Assert denial before credential use and no external call.
Avoid logging token values to prove this. Record a non-secret credential reference or have the fake transport inspect a test-only handle.
Selection can be a real product feature
An operator may legitimately choose among several authorized accounts. Offer those choices and validate the selected identity. The server still decides which credential can be used for which action.
That boundary should stay the same whether the account was selected in a dropdown, written in a prompt, or returned by a model. Generated text is a request to act, not a credential delegation mechanism.
Check the selected credential reference, not a generated account label
The fixture stores account membership on the server. Alice belongs to tenant A and may use account A. Bob belongs to tenant B and may use account B. Model arguments cannot manufacture either identity. The context fields are private, and construction checks the trusted registry before any connector reference is selected.
An allowed read of invoice 42 under Alice's context records credential-ref-a. A request for account B or an unknown account fails context creation. A generated nested credential override fails parsing. Supplying an actor override around an otherwise valid call also fails. None of these failures selects B's reference or falls back to a shared administrative reference.
The credential value is deliberately an opaque fixture reference, not a real secret. The test can therefore assert selection without printing a token. In a deployed system, resolve the reference through a server-owned credential store only after authorization, and avoid including the resolved secret in events or model-visible errors.
The gateway rechecks policy when it executes. The test revokes Alice after constructing a valid context, then tries another read. It is denied before another credential use. A context is a carrier of checked scope, not a promise that a revoked grant stays valid forever.
The shared Rust gateway and its dispatch tests make the selection point visible. Authentication is a fixture token registry, so the example does not test an identity provider, session security, or secret-manager access controls.
Account selection can still be a user feature. Treat the requested account as an input to authorize against current membership. Return a safe denial when it is unavailable. The dangerous shortcut is letting an untrusted proposal become the authority that chooses an arbitrary credential. Keep the identity, membership decision, and selected reference connected all the way to the transport boundary.