Build1 publisher2 min readPublished
Suspending a Kinde user leaves their agent's signed token verifying mid-task
A developer suspended a signed-in test user while that user's Claude agent was still running, and the app kept reading the token as valid, so enforcement has to move into a status check the application itself owns.
The Engineer · Build desk

What happened
- A developer signed a real test user into a Kinde-backed app, handed that user's agent a task, and then suspended the same user in Kinde's dashboard while the run was still in progress.
- The agent carried on working through the task after the suspension.
- The app's own check kept reporting the suspended user's access token as valid, seconds after Kinde had already suspended them.
- The fix pairs a Kinde webhook that updates the app's own user record with a check that runs before every single agent action against that record.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Nothing can un-issue a signed token, so the only revocation an app can enforce costs either a lookup table covering every token ever minted or a status check the app runs itself.
- cost Enforcement is paid for in status reads, one per tool call, and it gives up the no-round-trip property that made signed tokens attractive.
- exposure Until the app's own record catches up, an offboarded person's agent holds a working credential for the rest of the token's minted lifetime, with the destructive write tool still in reach.
- decision Teams wiring agents to an identity provider have to decide which record is authoritative at the moment an action runs: the token from sign-in, or the app's current row for that user.
An OAuth access token is a small JSON payload with a cryptographic signature attached, and whatever checks it verifies that signature against a public key instead of calling the issuer to ask whether the token is still good [5]. An API can confirm a token is genuine without a database round trip on every request [6]. Suspending a user in Kinde changes a row in Kinde's own database, and the token was already signed and handed out before that write, so it verifies exactly as it did before [3].
The author, who publishes as sholajegede, wrote: "So that gap isn't a bug in Kinde, and it isn't a bug in OAuth either." [8] I think that is right. A stateless credential stays valid until whatever expiry it was minted with [17], and revoking one properly means tracking every issued token in a lookup table, the thing signed tokens exist to avoid, or waiting for the token to expire on its own [7].
The enforcement point in the demo app is a single function, enforceToolCall, which looks up the acting user's current status for every tool call the model makes [13]. The agent is a Claude Messages API loop with three tools against a demo set of internal resources: list_resources, read_resource and write_resource [10]. The registry marks write_resource as destructive: true and the other two as destructive: false [11], so two of the three registered actions are reads [16].
Both the tool schema handed to Claude and the enforcement check are built from the same registry [12], so the list the model is offered and the list the check knows about cannot drift apart. An action that is not in the table cannot be called by accident [14].
Whether this transfers depends on where you put the check. It has to sit on the path every tool call takes, because a long-running agent passes through session setup once and then loops; a check at the top of the loop reads a status that was fresh minutes ago. It also depends on delivery: the app's record only stops saying "active" once Kinde's webhook arrives [9]. Both conditions are properties of your own code.
The post says it reports webhook deliveries measured live, a production bug that could have left an offboarded user's record looking active forever, and a hard number for how long an offboarded person's agent keeps acting before anything catches it [15]. The text available stops at the enforcement function, before those figures. On the evidence here, the window is bounded by the token's minted expiry [17].
What to watch
- The webhook delivery times the post says it measured would bound how long an offboarded user's agent keeps acting.
- Whether a status lookup per tool call holds up in an agent loop that makes hundreds of calls per task.
- Whether the same check covers agents running in background jobs, where there is no fresh session to hang it on.