Can I prevent a replay attack of my signed JWTs?

The jti claim as described here is an optional mechanism for preventing further replay attacks. From the spec:

4.1.7. "jti" (JWT ID) Claim

The "jti" (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" claim can be used to prevent the JWT from being replayed. The "jti" value is a case- sensitive string. Use of this claim is OPTIONAL.

This does ultimately make your server stateful, but it prevents against unlimited replays if you detect anomalous behavior, or if a user reports suspicious activity. Consider the following scenario.

  1. A user logs in. Your server generates a JWT, and stores the signature as well as some metadata (the user id and the type of client making the request, perhaps, and the jti).
  2. User reports suspicious behavior.
  3. The application "signs out" the user of all devices by deleting all JWTs in the backend store attached to that user. Now the application can say "I know you've got a valid signature, but I'm not accepting it because I didn't create it."
    • If your metadata is precise enough, you can use the jti plus additional information to, say, only sign the user out of given devices.

As mentioned above, this does inevitably make your server stateful. This also doesn't outright prevent replay attacks, but it can shut down further such attacks after one has been detected.

An alternative/additional method CAN outright prevent replay attacks to some degree, at the risk of potential inconvenience to the user. Make the user's IP address part of the claim AND stored metadata upon login, and validate that the IP using the JWT is the one you expect. This can be frustrating for a user that say, both works from home and a coffee shop, but it might be an acceptable requirement for high-security applications.

If there is code executing in the same context of your web application, whether it was part of a XSS attack in the browser, or some malware/virus on the users machine, it would be difficult (impossible?) to differentiate those requests from your own.

If the attacker has access to the computer, they could also just steal the data from your applications normal requests to your server.

Potentially, you could some server side intrusion detection style analysis: e.g. check for a high number of requests or item counts; enforce some custom referrer scheme where you know which components of you web application make data requests. Then maybe trigger a reauthentication when you detect anomalous behavior? You would also need a server side way to expire your JWT above the exp embedded in the JWT.