Skip to content

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

Illustration accompanying Suspending a Kinde user leaves their agent's signed token verifying mid-task

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.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories