Securing APIs Against Replay Attacks with JWT Proof-of-Possession (PoP) Tokens

Sojess T SojanSojess T Sojan
4 min read

In modern web applications, especially those handling sensitive data or transactions, a common and dangerous threat is the replay attack. In its simplest form, a replay attack occurs when a malicious actor captures a valid request or token and resends it ("replays" it) to the server to impersonate a legitimate user.

The most widely known type is the session replay attack—commonly associated with session-based authentication where a hijacked cookie or session ID gives the attacker full access. However, even in stateless JWT-based authentication systems, replay attacks remain a serious concern if not properly handled.

JWTs are self-contained bearer tokens: they hold all required user information and are typically valid for a fixed period. If a JWT is stolen, it can be reused by an attacker until it expires, unless additional protection is added. This undermines the fundamental goal of secure, stateless API communication.

This is where JWT Proof-of-Possession (PoP) tokens come in—a lightweight, elegant solution that ensures not just possession of the token, but also possession of a cryptographic key. Let's dive into how JWT PoP works and how it mitigates replay attacks effectively.

How JWT Proof-of-Possession (PoP) Tokens Work

Unlike traditional bearer tokens, PoP tokens require proof that the client not only holds the token—but also possesses the private key associated with it. This additional cryptographic check raises the bar for attackers and adds a meaningful layer of defense.

Let’s break down the flow:

1. Key Generation: The Foundation of Trust

Before anything else, the client (usually a mobile app or SPA) generates a public-private key pair.

  • The private key is kept securely on the client device.

  • The public key is either pre-registered or sent to the Authorization Server as part of the access token request.

This asymmetric key pair is at the heart of PoP—without the private key, the token is useless.

2.Requesting the Access Token

When the client wants to authenticate and access resources, it sends an access token request to the Authorization Server. This step includes:

  • Standard authentication (e.g., OAuth2 authorization code, client credentials, etc.)

  • Public key transmission if not already registered.

The Authorization Server validates the request and, if everything checks out, issues a JWT access token bound to the client’s public key.

3. Issuing the PoP Token: JWT with a Twist

What makes this JWT special is the cnf (confirmation) claim embedded in the payload. This claim binds the token to the public key the client provided.

Example JWT Payload:

{
  "iss": "https://auth.example.com",
  "sub": "user123",
  "aud": "https://api.example.com",
  "exp": 1712000000,
  "iat": 1711990000,
  "cnf": {
    "jwk": {
      "kty": "RSA",
      "e": "AQAB",
      "n": "3v9c...xyz"
    }
  }
}

This means: Only the entity possessing the corresponding private key can use this token.

4. Making a PoP-Protected API Request

The client is now ready to access the resource server (API). But to do so securely, it must prove possession of the private key. Here’s how:

  • Step 1: Sign parts of the HTTP request (like method, URL, timestamp) using the private key.

  • Step 2: Send the signed JWT (PoP token) in the Authorization header.

  • Step 3: Attach the digital signature (PoP proof) in a separate header (e.g., PoP-Signature).

Sample Headers:

Authorization: PoP eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
PoP-Signature: MIIQ...ABC123

This signature proves that the client holds the private key tied to the token—something a thief with just the token can’t forge.

5. Verifying the PoP Token on the Server

The resource server now plays detective:

  • It validates the JWT (issuer, expiration, audience).

  • It extracts the public key from the cnf claim.

  • It verifies the signature in PoP-Signature using the public key.

If everything lines up, the request is considered authentic. If not, it’s rejected outright.

6.Access Granted… or Denied

If the proof-of-possession checks out, the API processes the request as coming from a verified client. If not, it denies access, effectively shutting down attempts to use stolen tokens without the private key.

Why JWT PoP Tokens Matter

Prevent Token Misuse

PoP tokens make it nearly impossible to use a stolen token without the private key. Even if an attacker intercepts the token, they can’t generate valid request signatures.

Stop Replay Attacks

PoP signatures often include nonces or timestamps, meaning each request is unique. This renders replay attempts useless—expired or reused signatures simply won’t validate.

Fits Stateless APIs

Because the cryptographic proof is self-contained, PoP tokens work beautifully in stateless environments like microservices or serverless APIs—no session tracking or token introspection required.

Conclusion

JWT PoP tokens are a practical and robust enhancement for modern API security. They offer:

  • Strong protection against stolen token misuse

  • Minimal impact on stateless API architecture

  • Flexibility across OAuth2 and custom auth implementations

If you’re building APIs where data sensitivity, regulatory compliance, or zero-trust architectures are in play, JWT PoP tokens are well worth considering.

0
Subscribe to my newsletter

Read articles from Sojess T Sojan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sojess T Sojan
Sojess T Sojan